diff --git a/commands.go b/commands.go index 6659252bef..0933171448 100644 --- a/commands.go +++ b/commands.go @@ -803,45 +803,27 @@ func (srv *Server) CmdTag(stdin io.ReadCloser, stdout io.Writer, args ...string) } func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) error { - cmd := rcli.Subcmd(stdout, "run", "[OPTIONS] IMAGE COMMAND [ARG...]", "Run a command in a new container") - fl_user := cmd.String("u", "", "Username or UID") - fl_detach := cmd.Bool("d", false, "Detached mode: leave the container running in the background") - fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached") - fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty") - fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)") - var fl_ports ports - - cmd.Var(&fl_ports, "p", "Map a network port to the container") - var fl_env ListOpts - cmd.Var(&fl_env, "e", "Set environment variables") - if err := cmd.Parse(args); err != nil { - return nil + config, err := ParseRun(args) + if err != nil { + return err } - if cmd.NArg() < 2 { - cmd.Usage() - return nil + if config.Image == "" { + return fmt.Errorf("Image not specified") + } + if len(config.Cmd) == 0 { + return fmt.Errorf("Command not specified") } - name := cmd.Arg(0) - cmdline := cmd.Args()[1:] // Create new container - container, err := srv.runtime.Create(cmdline[0], cmdline[1:], name, - &Config{ - Ports: fl_ports, - User: *fl_user, - Tty: *fl_tty, - OpenStdin: *fl_stdin, - Memory: *fl_memory, - Env: fl_env, - }) + container, err := srv.runtime.Create(config) if err != nil { return errors.New("Error creating container: " + err.Error()) } - if *fl_stdin { + if config.OpenStdin { cmd_stdin, err := container.StdinPipe() if err != nil { return err } - if !*fl_detach { + if !config.Detach { Go(func() error { _, err := io.Copy(cmd_stdin, stdin) cmd_stdin.Close() @@ -850,7 +832,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string) } } // Run the container - if !*fl_detach { + if !config.Detach { cmd_stderr, err := container.StderrPipe() if err != nil { return err diff --git a/container.go b/container.go index 2b94fa7e00..f900599d00 100644 --- a/container.go +++ b/container.go @@ -3,6 +3,7 @@ package docker import ( "encoding/json" "errors" + "flag" "fmt" "github.com/kr/pty" "io" @@ -50,10 +51,43 @@ type Config struct { User string Memory int64 // Memory limit (in bytes) MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap + Detach bool Ports []int Tty bool // Attach standard streams to a tty, including stdin if it is not closed. OpenStdin bool // Open stdin Env []string + Cmd []string + Image string // Name of the image as it was passed by the operator (eg. could be symbolic) +} + +func ParseRun(args []string) (*Config, error) { + cmd := flag.NewFlagSet("", flag.ContinueOnError) + cmd.SetOutput(ioutil.Discard) + fl_user := cmd.String("u", "", "Username or UID") + fl_detach := cmd.Bool("d", false, "Detached mode: leave the container running in the background") + fl_stdin := cmd.Bool("i", false, "Keep stdin open even if not attached") + fl_tty := cmd.Bool("t", false, "Allocate a pseudo-tty") + fl_memory := cmd.Int64("m", 0, "Memory limit (in bytes)") + var fl_ports ports + + cmd.Var(&fl_ports, "p", "Map a network port to the container") + var fl_env ListOpts + cmd.Var(&fl_env, "e", "Set environment variables") + if err := cmd.Parse(args); err != nil { + return nil, err + } + config := &Config{ + Ports: fl_ports, + User: *fl_user, + Tty: *fl_tty, + OpenStdin: *fl_stdin, + Memory: *fl_memory, + Detach: *fl_detach, + Env: fl_env, + Cmd: cmd.Args()[1:], + Image: cmd.Arg(0), + } + return config, nil } type NetworkSettings struct { diff --git a/container_test.go b/container_test.go index 94db11408d..3f9b02ab58 100644 --- a/container_test.go +++ b/container_test.go @@ -20,10 +20,9 @@ func TestCommitRun(t *testing.T) { } defer nuke(runtime) container1, err := runtime.Create( - "/bin/sh", - []string{"-c", "echo hello > /world"}, - GetTestImage(runtime).Id, &Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"/bin/sh", "-c", "echo hello > /world"}, Memory: 33554432, }, ) @@ -54,11 +53,10 @@ func TestCommitRun(t *testing.T) { // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world container2, err := runtime.Create( - "cat", - []string{"/world"}, - img.Id, &Config{ + Image: img.Id, Memory: 33554432, + Cmd: []string{"cat", "/world"}, }, ) if err != nil { @@ -88,11 +86,10 @@ func TestRun(t *testing.T) { } defer nuke(runtime) container, err := runtime.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime).Id, &Config{ + Image: GetTestImage(runtime).Id, Memory: 33554432, + Cmd: []string{"ls", "-al"}, }, ) if err != nil { @@ -118,10 +115,10 @@ func TestOutput(t *testing.T) { } defer nuke(runtime) container, err := runtime.Create( - "echo", - []string{"-n", "foobar"}, - GetTestImage(runtime).Id, - &Config{}, + &Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"echo", "-n", "foobar"}, + }, ) if err != nil { t.Fatal(err) @@ -142,11 +139,10 @@ func TestKill(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "cat", - []string{"/dev/zero"}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"cat", "/dev/zero"}, + }, ) if err != nil { t.Fatal(err) @@ -185,11 +181,11 @@ func TestExitCode(t *testing.T) { } defer nuke(runtime) - trueContainer, err := runtime.Create( - "/bin/true", - []string{""}, - GetTestImage(runtime).Id, - &Config{}, + trueContainer, err := runtime.Create(&Config{ + + Image: GetTestImage(runtime).Id, + Cmd: []string{"/bin/true", ""}, + }, ) if err != nil { t.Fatal(err) @@ -199,11 +195,10 @@ func TestExitCode(t *testing.T) { t.Fatal(err) } - falseContainer, err := runtime.Create( - "/bin/false", - []string{""}, - GetTestImage(runtime).Id, - &Config{}, + falseContainer, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"/bin/false", ""}, + }, ) if err != nil { t.Fatal(err) @@ -228,11 +223,10 @@ func TestRestart(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "echo", - []string{"-n", "foobar"}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"echo", "-n", "foobar"}, + }, ) if err != nil { t.Fatal(err) @@ -262,13 +256,12 @@ func TestRestartStdin(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "cat", - []string{}, - GetTestImage(runtime).Id, - &Config{ - OpenStdin: true, - }, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"cat"}, + + OpenStdin: true, + }, ) if err != nil { t.Fatal(err) @@ -313,11 +306,10 @@ func TestUser(t *testing.T) { defer nuke(runtime) // Default user must be root - container, err := runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"id"}, + }, ) if err != nil { t.Fatal(err) @@ -332,13 +324,12 @@ func TestUser(t *testing.T) { } // Set a username - container, err = runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{ - User: "root", - }, + container, err = runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"id"}, + + User: "root", + }, ) if err != nil { t.Fatal(err) @@ -353,13 +344,12 @@ func TestUser(t *testing.T) { } // Set a UID - container, err = runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{ - User: "0", - }, + container, err = runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"id"}, + + User: "0", + }, ) if err != nil || container.State.ExitCode != 0 { t.Fatal(err) @@ -374,13 +364,12 @@ func TestUser(t *testing.T) { } // Set a different user by uid - container, err = runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{ - User: "1", - }, + container, err = runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"id"}, + + User: "1", + }, ) if err != nil { t.Fatal(err) @@ -397,13 +386,12 @@ func TestUser(t *testing.T) { } // Set a different user by username - container, err = runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{ - User: "daemon", - }, + container, err = runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"id"}, + + User: "daemon", + }, ) if err != nil { t.Fatal(err) @@ -425,22 +413,20 @@ func TestMultipleContainers(t *testing.T) { } defer nuke(runtime) - container1, err := runtime.Create( - "cat", - []string{"/dev/zero"}, - GetTestImage(runtime).Id, - &Config{}, + container1, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"cat", "/dev/zero"}, + }, ) if err != nil { t.Fatal(err) } defer runtime.Destroy(container1) - container2, err := runtime.Create( - "cat", - []string{"/dev/zero"}, - GetTestImage(runtime).Id, - &Config{}, + container2, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"cat", "/dev/zero"}, + }, ) if err != nil { t.Fatal(err) @@ -479,13 +465,12 @@ func TestStdin(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "cat", - []string{}, - GetTestImage(runtime).Id, - &Config{ - OpenStdin: true, - }, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"cat"}, + + OpenStdin: true, + }, ) if err != nil { t.Fatal(err) @@ -514,13 +499,12 @@ func TestTty(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "cat", - []string{}, - GetTestImage(runtime).Id, - &Config{ - OpenStdin: true, - }, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"cat"}, + + OpenStdin: true, + }, ) if err != nil { t.Fatal(err) @@ -549,11 +533,10 @@ func TestEnv(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "/usr/bin/env", - []string{}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"/usr/bin/env"}, + }, ) if err != nil { t.Fatal(err) @@ -623,14 +606,13 @@ func TestLXCConfig(t *testing.T) { memMin := 33554432 memMax := 536870912 mem := memMin + rand.Intn(memMax-memMin) - container, err := runtime.Create( - "/bin/true", - []string{}, - GetTestImage(runtime).Id, - &Config{ - Hostname: "foobar", - Memory: int64(mem), - }, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"/bin/true"}, + + Hostname: "foobar", + Memory: int64(mem), + }, ) if err != nil { t.Fatal(err) @@ -651,11 +633,10 @@ func BenchmarkRunSequencial(b *testing.B) { } defer nuke(runtime) for i := 0; i < b.N; i++ { - container, err := runtime.Create( - "echo", - []string{"-n", "foo"}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"echo", "-n", "foo"}, + }, ) if err != nil { b.Fatal(err) @@ -687,11 +668,10 @@ func BenchmarkRunParallel(b *testing.B) { complete := make(chan error) tasks = append(tasks, complete) go func(i int, complete chan error) { - container, err := runtime.Create( - "echo", - []string{"-n", "foo"}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"echo", "-n", "foo"}, + }, ) if err != nil { complete <- err diff --git a/runtime.go b/runtime.go index 21e1466f51..da95dfe365 100644 --- a/runtime.go +++ b/runtime.go @@ -64,21 +64,20 @@ func (runtime *Runtime) containerRoot(id string) string { return path.Join(runtime.repository, id) } -func (runtime *Runtime) Create(command string, args []string, image string, config *Config) (*Container, error) { +func (runtime *Runtime) Create(config *Config) (*Container, error) { // Lookup image - img, err := runtime.repositories.LookupImage(image) + img, err := runtime.repositories.LookupImage(config.Image) if err != nil { return nil, err } container := &Container{ // FIXME: we should generate the ID here instead of receiving it as an argument - Id: GenerateId(), - Created: time.Now(), - Path: command, - Args: args, - Config: config, - Image: img.Id, // Always use the resolved image id - //FIXME: store the name under which the image was given, for reference + Id: GenerateId(), + Created: time.Now(), + Path: config.Cmd[0], + Args: config.Cmd[1:], //FIXME: de-duplicate from config + Config: config, + Image: img.Id, // Always use the resolved image id NetworkSettings: &NetworkSettings{}, // FIXME: do we need to store this in the container? SysInitPath: sysInitPath, diff --git a/runtime_test.go b/runtime_test.go index fe3ab4810f..76882f95a9 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -112,11 +112,10 @@ func TestRuntimeCreate(t *testing.T) { if len(runtime.List()) != 0 { t.Errorf("Expected 0 containers, %v found", len(runtime.List())) } - container, err := runtime.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) @@ -160,11 +159,10 @@ func TestDestroy(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container, err := runtime.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime).Id, - &Config{}, + container, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) @@ -208,33 +206,30 @@ func TestGet(t *testing.T) { t.Fatal(err) } defer nuke(runtime) - container1, err := runtime.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime).Id, - &Config{}, + container1, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) } defer runtime.Destroy(container1) - container2, err := runtime.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime).Id, - &Config{}, + container2, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) } defer runtime.Destroy(container2) - container3, err := runtime.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime).Id, - &Config{}, + container3, err := runtime.Create(&Config{ + Image: GetTestImage(runtime).Id, + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) @@ -274,11 +269,10 @@ func TestRestore(t *testing.T) { } // Create a container with one instance of docker - container1, err := runtime1.Create( - "ls", - []string{"-al"}, - GetTestImage(runtime1).Id, - &Config{}, + container1, err := runtime1.Create(&Config{ + Image: GetTestImage(runtime1).Id, + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err)