diff --git a/api/container/container.proto b/api/container/container.proto index bed9c28..2a271ab 100644 --- a/api/container/container.proto +++ b/api/container/container.proto @@ -25,13 +25,21 @@ service Containers { message Container { string id = 1; - string image = 2; + repeated Mount mounts = 2; repeated string tags = 3; Process process = 4; - optional string container_spec = 5; } message Process { + uint64 pid = 1; + repeated string args = 1; + repeated string env = 2; + User user = 3; + string cwd = 4; + bool terminal = 5; +} + +message ProcessSpec { repeated string args = 1; repeated string env = 2; User user = 3; @@ -54,10 +62,10 @@ message User { message CreateRequest { string id = 1; - Process process = 2; + ProcessSpec process = 2; repeated Mount mounts = 3; - optional string config_path = 4; - repeated string tags = 5; + repeated string tags = 4; + optional string config_path = 5; } message CreateResponse { diff --git a/cli/ctr/main.go b/cli/ctr/main.go new file mode 100644 index 0000000..cc142ac --- /dev/null +++ b/cli/ctr/main.go @@ -0,0 +1,49 @@ +package main + +import ( + "fmt" + "os" + + "github.com/Sirupsen/logrus" + "github.com/docker/containerd" + "github.com/urfave/cli" +) + +func main() { + app := cli.NewApp() + app.Name = "ctr" + app.Version = containerd.Version + app.Usage = ` + __ + _____/ /______ + / ___/ __/ ___/ +/ /__/ /_/ / +\___/\__/_/ + +containerd client +` + app.Flags = []cli.Flag{ + cli.BoolFlag{ + Name: "debug", + Usage: "enable debug output in logs", + }, + cli.StringFlag{ + Name: "socket, s", + Usage: "socket path for containerd's GRPC server", + Value: "/run/containerd/containerd.sock", + }, + } + app.Commands = []cli.Command{ + runCommand, + } + 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 { + fmt.Fprintf(os.Stderr, "containerd: %s\n", err) + os.Exit(1) + } +} diff --git a/cli/ctr/run.go b/cli/ctr/run.go new file mode 100644 index 0000000..2fbe848 --- /dev/null +++ b/cli/ctr/run.go @@ -0,0 +1,85 @@ +package main + +import ( + "fmt" + "os" + + "github.com/BurntSushi/toml" + "github.com/urfave/cli" +) + +type runConfig struct { + Image string `toml:"image"` + Process struct { + Args []string `toml:"args"` + Env []string `toml:"env"` + Cwd string `toml:"cwd"` + Uid int `toml:"uid"` + Gid int `toml:"gid"` + Tty bool `toml:"tty"` + } `toml:"process"` + Network struct { + Type string `toml:"type"` + IP string `toml:"ip"` + Gateway string `toml:"gateway"` + } `toml:"network"` +} + +var runCommand = cli.Command{ + Name: "run", + Usage: "run a container", + Action: func(context *cli.Context) error { + var config runConfig + if _, err := toml.DecodeFile(context.Args().First(), &config); err != nil { + return err + } + id := context.Args().Get(1) + if id == "" { + return fmt.Errorf("containerd must be provided") + } + client, err := getClient(context) + if err != nil { + return err + } + clone, err := client.Images.Clone(config.Image) + if err != nil { + return err + } + container, err := client.Containers.Create(CreateRequest{ + Id: id, + Mounts: clone.Mounts, + Process: Process{ + Args: config.Args, + Env: config.Env, + Cwd: config.Cwd, + Uid: config.Uid, + Gid: config.Gid, + Tty: config.Tty, + Stdin: os.Stdin.Name(), + Stdout: os.Stdout.Name(), + Stderr: os.Stderr.Name(), + }, + Tags: []string{ + "ctr", + }, + }) + defer client.Containers.Delete(container) + if err := client.Networks.Attach(config.Network, container); err != nil { + return err + } + if err := client.Containers.Start(container); err != nil { + return err + } + go forwarSignals(client.Containers.SignalProcess, container.Process) + events, err := client.Containers.Events(container.Id) + if err != nil { + return err + } + for event := range events { + if event.Type == "exit" { + os.Exit(event.Status) + } + } + return nil + }, +}