diff --git a/api/v1/server.go b/api/v1/server.go index 724bff0..c0211ca 100644 --- a/api/v1/server.go +++ b/api/v1/server.go @@ -3,6 +3,8 @@ package v1 import ( "encoding/json" "net/http" + "strconv" + "syscall" "github.com/Sirupsen/logrus" "github.com/crosbymichael/containerd" @@ -15,9 +17,10 @@ func NewServer(supervisor *containerd.Supervisor) http.Handler { supervisor: supervisor, r: r, } - r.HandleFunc("/containers", s.containers).Methods("GET") + r.HandleFunc("/containers/{id:.*}/process/{pid:.*}", s.signalPid).Methods("POST") r.HandleFunc("/containers/{id:.*}", s.createContainer).Methods("POST") - r.HandleFunc("/containers/{id:.*}", s.deleteContainer).Methods("DELETE") + r.HandleFunc("/containers", s.containers).Methods("GET") + // r.HandleFunc("/containers/{id:.*}", s.deleteContainer).Methods("DELETE") return s } @@ -30,6 +33,39 @@ func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { s.r.ServeHTTP(w, r) } +func (s *server) signalPid(w http.ResponseWriter, r *http.Request) { + var ( + vars = mux.Vars(r) + id = vars["id"] + spid = vars["pid"] + ) + pid, err := strconv.Atoi(spid) + if err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + var signal Signal + if err := json.NewDecoder(r.Body).Decode(&signal); err != nil { + http.Error(w, err.Error(), http.StatusBadRequest) + return + } + e := &containerd.SignalEvent{ + ID: id, + Pid: pid, + Signal: syscall.Signal(signal.Signal), + Err: make(chan error, 1), + } + s.supervisor.SendEvent(e) + if err := <-e.Err; err != nil { + status := http.StatusInternalServerError + if err == containerd.ErrContainerNotFound { + status = http.StatusNotFound + } + http.Error(w, err.Error(), status) + return + } +} + func (s *server) containers(w http.ResponseWriter, r *http.Request) { var state State state.Containers = []Container{} @@ -49,10 +85,14 @@ func (s *server) containers(w http.ResponseWriter, r *http.Request) { "container": c.ID(), }).Error("get processes for container") } + var pids []int + for _, p := range processes { + pids = append(pids, p.Pid()) + } state.Containers = append(state.Containers, Container{ ID: c.ID(), BundlePath: c.Path(), - Processes: processes, + Processes: pids, }) } if err := json.NewEncoder(w).Encode(&state); err != nil { @@ -61,14 +101,6 @@ func (s *server) containers(w http.ResponseWriter, r *http.Request) { } } -func (s *server) events(w http.ResponseWriter, r *http.Request) { - -} - -func (s *server) deleteContainer(w http.ResponseWriter, r *http.Request) { - -} - func (s *server) createContainer(w http.ResponseWriter, r *http.Request) { id := mux.Vars(r)["id"] var c Container @@ -76,6 +108,10 @@ func (s *server) createContainer(w http.ResponseWriter, r *http.Request) { http.Error(w, err.Error(), http.StatusBadRequest) return } + if c.BundlePath == "" { + http.Error(w, "empty bundle path", http.StatusBadRequest) + return + } e := &containerd.StartContainerEvent{ ID: id, BundlePath: c.BundlePath, diff --git a/api/v1/types.go b/api/v1/types.go index bf16d71..4ca8fdb 100644 --- a/api/v1/types.go +++ b/api/v1/types.go @@ -9,3 +9,7 @@ type Container struct { BundlePath string `json:"bundlePath,omitempty"` Processes []int `json:"processes,omitempty"` } + +type Signal struct { + Signal int `json:"signal"` +} diff --git a/container.go b/container.go index 9e18a25..4fc8424 100644 --- a/container.go +++ b/container.go @@ -1,5 +1,12 @@ package containerd +import "os" + +type Process interface { + Pid() int + Signal(os.Signal) error +} + type Container interface { ID() string Start() error @@ -7,5 +14,5 @@ type Container interface { Pid() (int, error) SetExited(status int) Delete() error - Processes() ([]int, error) + Processes() ([]Process, error) } diff --git a/errors.go b/errors.go index 3632ad2..8cd3dc4 100644 --- a/errors.go +++ b/errors.go @@ -8,6 +8,7 @@ var ( ErrBundleNotFound = errors.New("containerd: bundle not found") ErrContainerNotFound = errors.New("containerd: container not found") ErrContainerExists = errors.New("containerd: container already exists") + ErrProcessNotFound = errors.New("containerd: processs not found for container") // Internal errors errShutdown = errors.New("containerd: supervisor is shutdown") diff --git a/event.go b/event.go index 8dd7260..c9490a8 100644 --- a/event.go +++ b/event.go @@ -1,5 +1,7 @@ package containerd +import "os" + type Event interface { String() string } @@ -44,3 +46,14 @@ type GetContainersEvent struct { func (c *GetContainersEvent) String() string { return "get containers" } + +type SignalEvent struct { + ID string + Pid int + Signal os.Signal + Err chan error +} + +func (s *SignalEvent) String() string { + return "signal event" +} diff --git a/runtime_linux.go b/runtime_linux.go index a7968dc..797b33b 100644 --- a/runtime_linux.go +++ b/runtime_linux.go @@ -158,6 +158,22 @@ func init() { } } +type libcontainerProcess struct { + pid int +} + +func (p *libcontainerProcess) Pid() int { + return p.pid +} + +func (p *libcontainerProcess) Signal(s os.Signal) error { + proc, err := os.FindProcess(p.pid) + if err != nil { + return err + } + return proc.Signal(s) +} + type libcontainerContainer struct { c libcontainer.Container initProcess *libcontainer.Process @@ -192,8 +208,18 @@ func (c *libcontainerContainer) Delete() error { return c.c.Destroy() } -func (c *libcontainerContainer) Processes() ([]int, error) { - return c.c.Processes() +func (c *libcontainerContainer) Processes() ([]Process, error) { + pids, err := c.c.Processes() + if err != nil { + return nil, err + } + var proceses []Process + for _, pid := range pids { + proceses = append(proceses, &libcontainerProcess{ + pid: pid, + }) + } + return proceses, nil } func NewRuntime(stateDir string) (Runtime, error) { diff --git a/supervisor.go b/supervisor.go index 798af1d..c62fd19 100644 --- a/supervisor.go +++ b/supervisor.go @@ -92,6 +92,24 @@ func (s *Supervisor) Start(events chan Event) error { e.Containers = append(e.Containers, c) } e.Err <- nil + case *SignalEvent: + container, ok := s.containers[e.ID] + if !ok { + e.Err <- ErrContainerNotFound + continue + } + processes, err := container.Processes() + if err != nil { + e.Err <- err + continue + } + for _, p := range processes { + if p.Pid() == e.Pid { + e.Err <- p.Signal(e.Signal) + continue + } + } + e.Err <- ErrProcessNotFound } } }()