diff --git a/api/v1/client.go b/api/v1/client.go new file mode 100644 index 000000000..876793c9a --- /dev/null +++ b/api/v1/client.go @@ -0,0 +1,70 @@ +package v1 + +import ( + "bytes" + "encoding/json" + "fmt" + "net/http" + "strconv" +) + +func NewClient(addr string) *Client { + return &Client{ + addr: addr, + } +} + +type Client struct { + addr string +} + +// Start starts a container with the specified id and path to the container's +// bundle on the system. +func (c *Client) Start(id, path string) error { + container := Container{ + BundlePath: path, + } + buf := bytes.NewBuffer(nil) + if err := json.NewEncoder(buf).Encode(container); err != nil { + return err + } + r, err := http.Post(c.addr+"/containers/"+id, "application/json", buf) + if err != nil { + return err + } + r.Body.Close() + if r.StatusCode != http.StatusCreated { + return fmt.Errorf("unexpected status %d", r.StatusCode) + } + return nil +} + +// Containers returns all running containers within containerd. +func (c *Client) Containers() ([]Container, error) { + r, err := http.Get(c.addr + "/containers") + if err != nil { + return nil, err + } + var s State + if err := json.NewDecoder(r.Body).Decode(&s); err != nil { + return nil, err + } + r.Body.Close() + return s.Containers, nil +} + +func (c *Client) Signal(id string, pid, signal int) error { + sig := Signal{ + Signal: signal, + } + buf := bytes.NewBuffer(nil) + if err := json.NewEncoder(buf).Encode(sig); err != nil { + return err + } + r, err := http.Post(c.addr+"/containers/"+id+"/process/"+strconv.Itoa(pid), "application/json", buf) + if err != nil { + return err + } + r.Body.Close() + return nil +} diff --git a/api/v1/server.go b/api/v1/server.go index 9ffe0aa19..47a7e30b8 100644 --- a/api/v1/server.go +++ b/api/v1/server.go @@ -27,6 +27,7 @@ func NewServer(supervisor *containerd.Supervisor) http.Handler { r.HandleFunc("/containers/{id:.*}/process", s.addProcess).Methods("PUT") r.HandleFunc("/containers/{id:.*}", s.createContainer).Methods("POST") r.HandleFunc("/containers/{id:.*}", s.updateContainer).Methods("PATCH") + // internal method for replaying the journal r.HandleFunc("/event", s.event).Methods("POST") r.HandleFunc("/events", s.events).Methods("GET") r.HandleFunc("/containers", s.containers).Methods("GET") diff --git a/containerd/main.go b/containerd/main.go index 53c12009b..d7be9273b 100644 --- a/containerd/main.go +++ b/containerd/main.go @@ -17,15 +17,12 @@ import ( "github.com/rcrowley/go-metrics" ) -const ( - Version = "0.0.1" - Usage = `High performance conatiner daemon` -) +const Usage = `High performance conatiner daemon` func main() { app := cli.NewApp() app.Name = "containerd" - app.Version = Version + app.Version = containerd.Version app.Usage = Usage app.Authors = []cli.Author{ { diff --git a/ctr/Makefile b/ctr/Makefile new file mode 100644 index 000000000..5f0348343 --- /dev/null +++ b/ctr/Makefile @@ -0,0 +1,2 @@ +all: + go build diff --git a/ctr/ctr b/ctr/ctr new file mode 100755 index 000000000..aa600d4a4 Binary files /dev/null and b/ctr/ctr differ diff --git a/ctr/main.go b/ctr/main.go new file mode 100644 index 000000000..a189d2cf7 --- /dev/null +++ b/ctr/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "os" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/docker/containerd" +) + +const Usage = `High performance conatiner daemon controller` + +func main() { + app := cli.NewApp() + app.Name = "ctr" + app.Version = containerd.Version + app.Usage = Usage + app.Authors = []cli.Author{ + { + Name: "@crosbymichael", + Email: "crosbymichael@gmail.com", + }, + } + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "enable debug output in the logs", + }, + cli.StringFlag{ + Name: "addr", + Value: "http://localhost:8888", + Usage: "address to the containerd api", + }, + } + app.Commands = []cli.Command{ + ContainersCommand, + } + app.Before = func(context *cli.Context) error { + if context.GlobalBool("debug") { + logrus.SetLevel(logrus.DebugLevel) + } + return nil + } + if err := app.Run(os.Args); err != nil { + logrus.Fatal(err) + } +} + +func fatal(err string, code int) { + fmt.Fprintf(os.Stderr, "[ctr] %s", err) + os.Exit(code) +} diff --git a/ctr/start.go b/ctr/start.go new file mode 100644 index 000000000..9cef47aa3 --- /dev/null +++ b/ctr/start.go @@ -0,0 +1,96 @@ +package main + +import ( + "fmt" + "os" + "text/tabwriter" + + "github.com/Sirupsen/logrus" + "github.com/codegangsta/cli" + "github.com/docker/containerd/api/v1" +) + +var ContainersCommand = cli.Command{ + Name: "containers", + Usage: "interact with running containers", + Subcommands: []cli.Command{ + StartCommand, + ListCommand, + KillCommand, + }, + Action: listContainers, +} + +var ListCommand = cli.Command{ + Name: "list", + Usage: "list all running containers", + Action: listContainers, +} + +func listContainers(context *cli.Context) { + c := v1.NewClient(context.GlobalString("addr")) + containers, err := c.Containers() + if err != nil { + fatal(err.Error(), 1) + } + w := tabwriter.NewWriter(os.Stdout, 20, 1, 3, ' ', 0) + fmt.Fprint(w, "ID\tPATH\tSTATUS\n") + for _, c := range containers { + fmt.Fprintf(w, "%s\t%s\t%s\n", c.ID, c.BundlePath, c.State.Status) + } + if err := w.Flush(); err != nil { + logrus.Fatal(err) + } +} + +var StartCommand = cli.Command{ + Name: "start", + Usage: "start a container", + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "id", + Value: "", + Usage: "id of the container", + }, + }, + Action: func(context *cli.Context) { + path := context.Args().First() + if path == "" { + fatal("bundle path cannot be empty", 1) + } + id := context.String("id") + if id == "" { + fatal("container id cannot be empty", 1) + } + c := v1.NewClient(context.GlobalString("addr")) + if err := c.Start(id, path); err != nil { + fatal(err.Error(), 1) + } + }, +} + +var KillCommand = cli.Command{ + Name: "kill", + Usage: "send a signal to a container or it's processes", + Flags: []cli.Flag{ + cli.IntFlag{ + Name: "pid,p", + Usage: "pid of the process to signal within the container", + }, + cli.IntFlag{ + Name: "signal,s", + Value: 15, + Usage: "signal to send to the container", + }, + }, + Action: func(context *cli.Context) { + id := context.Args().First() + if id == "" { + fatal("container id cannot be empty", 1) + } + c := v1.NewClient(context.GlobalString("addr")) + if err := c.Signal(id, context.Int("pid"), context.Int("signal")); err != nil { + fatal(err.Error(), 1) + } + }, +} diff --git a/version.go b/version.go new file mode 100644 index 000000000..1c528cd04 --- /dev/null +++ b/version.go @@ -0,0 +1,3 @@ +package containerd + +const Version = "0.0.1"