diff --git a/commands.go b/commands.go index 6659252bef..5dade37a96 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 + image, config, err := ParseRun(args) + if err != nil { + return err } - if cmd.NArg() < 2 { - cmd.Usage() - return nil + if 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(image, 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..afbf14e18f 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,42 @@ 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 +} + +func ParseRun(args []string) (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 + } + image := cmd.Arg(0) + 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:], + } + return image, config, nil } type NetworkSettings struct { diff --git a/container_test.go b/container_test.go index 94db11408d..e9e6992b7b 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{ + 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{ 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{ 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{ + Cmd: []string{"echo", "-n", "foobar"}, + }, ) if err != nil { t.Fatal(err) @@ -142,11 +139,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"cat", "/dev/zero"}, + }, ) if err != nil { t.Fatal(err) @@ -185,11 +180,9 @@ func TestExitCode(t *testing.T) { } defer nuke(runtime) - trueContainer, err := runtime.Create( - "/bin/true", - []string{""}, - GetTestImage(runtime).Id, - &Config{}, + trueContainer, err := runtime.Create(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"/bin/true", ""}, + }, ) if err != nil { t.Fatal(err) @@ -199,11 +192,9 @@ func TestExitCode(t *testing.T) { t.Fatal(err) } - falseContainer, err := runtime.Create( - "/bin/false", - []string{""}, - GetTestImage(runtime).Id, - &Config{}, + falseContainer, err := runtime.Create(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"/bin/false", ""}, + }, ) if err != nil { t.Fatal(err) @@ -228,11 +219,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"echo", "-n", "foobar"}, + }, ) if err != nil { t.Fatal(err) @@ -262,13 +251,11 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"cat"}, + + OpenStdin: true, + }, ) if err != nil { t.Fatal(err) @@ -313,11 +300,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"id"}, + }, ) if err != nil { t.Fatal(err) @@ -332,13 +317,11 @@ func TestUser(t *testing.T) { } // Set a username - container, err = runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{ - User: "root", - }, + container, err = runtime.Create(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"id"}, + + User: "root", + }, ) if err != nil { t.Fatal(err) @@ -353,13 +336,11 @@ func TestUser(t *testing.T) { } // Set a UID - container, err = runtime.Create( - "id", - []string{}, - GetTestImage(runtime).Id, - &Config{ - User: "0", - }, + container, err = runtime.Create(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"id"}, + + User: "0", + }, ) if err != nil || container.State.ExitCode != 0 { t.Fatal(err) @@ -374,13 +355,11 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"id"}, + + User: "1", + }, ) if err != nil { t.Fatal(err) @@ -397,13 +376,11 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"id"}, + + User: "daemon", + }, ) if err != nil { t.Fatal(err) @@ -425,22 +402,18 @@ func TestMultipleContainers(t *testing.T) { } defer nuke(runtime) - container1, err := runtime.Create( - "cat", - []string{"/dev/zero"}, - GetTestImage(runtime).Id, - &Config{}, + container1, err := runtime.Create(GetTestImage(runtime).Id, &Config{ + 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"cat", "/dev/zero"}, + }, ) if err != nil { t.Fatal(err) @@ -479,13 +452,11 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"cat"}, + + OpenStdin: true, + }, ) if err != nil { t.Fatal(err) @@ -514,13 +485,11 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"cat"}, + + OpenStdin: true, + }, ) if err != nil { t.Fatal(err) @@ -549,11 +518,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"/usr/bin/env"}, + }, ) if err != nil { t.Fatal(err) @@ -623,14 +590,12 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"/bin/true"}, + + Hostname: "foobar", + Memory: int64(mem), + }, ) if err != nil { t.Fatal(err) @@ -651,11 +616,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"echo", "-n", "foo"}, + }, ) if err != nil { b.Fatal(err) @@ -687,11 +650,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"echo", "-n", "foo"}, + }, ) if err != nil { complete <- err diff --git a/runtime.go b/runtime.go index 21e1466f51..a9efe446d7 100644 --- a/runtime.go +++ b/runtime.go @@ -64,7 +64,7 @@ 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(image string, config *Config) (*Container, error) { // Lookup image img, err := runtime.repositories.LookupImage(image) if err != nil { @@ -72,13 +72,12 @@ func (runtime *Runtime) Create(command string, args []string, image string, conf } 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..a2d1e68a63 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -112,11 +112,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) @@ -160,11 +158,9 @@ 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) @@ -208,33 +204,27 @@ 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(GetTestImage(runtime).Id, &Config{ + 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(GetTestImage(runtime).Id, &Config{ + 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(GetTestImage(runtime).Id, &Config{ + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err) @@ -274,11 +264,9 @@ 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(GetTestImage(runtime1).Id, &Config{ + Cmd: []string{"ls", "-al"}, + }, ) if err != nil { t.Fatal(err)