runtime.Create receives an image name + Config. The Config includes all required runtime information: command, environment, ports etc.

This commit is contained in:
Solomon Hykes 2013-03-23 12:16:58 -07:00
parent 0c4b639083
commit 031f91df1a
5 changed files with 147 additions and 184 deletions

View File

@ -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 { 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") image, config, err := ParseRun(args)
fl_user := cmd.String("u", "", "Username or UID") if err != nil {
fl_detach := cmd.Bool("d", false, "Detached mode: leave the container running in the background") return err
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
} }
if cmd.NArg() < 2 { if image == "" {
cmd.Usage() return fmt.Errorf("Image not specified")
return nil }
if len(config.Cmd) == 0 {
return fmt.Errorf("Command not specified")
} }
name := cmd.Arg(0)
cmdline := cmd.Args()[1:]
// Create new container // Create new container
container, err := srv.runtime.Create(cmdline[0], cmdline[1:], name, container, err := srv.runtime.Create(image, config)
&Config{
Ports: fl_ports,
User: *fl_user,
Tty: *fl_tty,
OpenStdin: *fl_stdin,
Memory: *fl_memory,
Env: fl_env,
})
if err != nil { if err != nil {
return errors.New("Error creating container: " + err.Error()) return errors.New("Error creating container: " + err.Error())
} }
if *fl_stdin { if config.OpenStdin {
cmd_stdin, err := container.StdinPipe() cmd_stdin, err := container.StdinPipe()
if err != nil { if err != nil {
return err return err
} }
if !*fl_detach { if !config.Detach {
Go(func() error { Go(func() error {
_, err := io.Copy(cmd_stdin, stdin) _, err := io.Copy(cmd_stdin, stdin)
cmd_stdin.Close() cmd_stdin.Close()
@ -850,7 +832,7 @@ func (srv *Server) CmdRun(stdin io.ReadCloser, stdout io.Writer, args ...string)
} }
} }
// Run the container // Run the container
if !*fl_detach { if !config.Detach {
cmd_stderr, err := container.StderrPipe() cmd_stderr, err := container.StderrPipe()
if err != nil { if err != nil {
return err return err

View File

@ -3,6 +3,7 @@ package docker
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"flag"
"fmt" "fmt"
"github.com/kr/pty" "github.com/kr/pty"
"io" "io"
@ -50,10 +51,42 @@ type Config struct {
User string User string
Memory int64 // Memory limit (in bytes) Memory int64 // Memory limit (in bytes)
MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap MemorySwap int64 // Total memory usage (memory + swap); set `-1' to disable swap
Detach bool
Ports []int Ports []int
Tty bool // Attach standard streams to a tty, including stdin if it is not closed. Tty bool // Attach standard streams to a tty, including stdin if it is not closed.
OpenStdin bool // Open stdin OpenStdin bool // Open stdin
Env []string 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 { type NetworkSettings struct {

View File

@ -20,10 +20,9 @@ func TestCommitRun(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container1, err := runtime.Create( container1, err := runtime.Create(
"/bin/sh",
[]string{"-c", "echo hello > /world"},
GetTestImage(runtime).Id, GetTestImage(runtime).Id,
&Config{ &Config{
Cmd: []string{"/bin/sh", "-c", "echo hello > /world"},
Memory: 33554432, 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 // FIXME: Make a TestCommit that stops here and check docker.root/layers/img.id/world
container2, err := runtime.Create( container2, err := runtime.Create(
"cat",
[]string{"/world"},
img.Id, img.Id,
&Config{ &Config{
Memory: 33554432, Memory: 33554432,
Cmd: []string{"cat", "/world"},
}, },
) )
if err != nil { if err != nil {
@ -88,11 +86,10 @@ func TestRun(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(
"ls",
[]string{"-al"},
GetTestImage(runtime).Id, GetTestImage(runtime).Id,
&Config{ &Config{
Memory: 33554432, Memory: 33554432,
Cmd: []string{"ls", "-al"},
}, },
) )
if err != nil { if err != nil {
@ -118,10 +115,10 @@ func TestOutput(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(
"echo",
[]string{"-n", "foobar"},
GetTestImage(runtime).Id, GetTestImage(runtime).Id,
&Config{}, &Config{
Cmd: []string{"echo", "-n", "foobar"},
},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -142,11 +139,9 @@ func TestKill(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"cat", Cmd: []string{"cat", "/dev/zero"},
[]string{"/dev/zero"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -185,11 +180,9 @@ func TestExitCode(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
trueContainer, err := runtime.Create( trueContainer, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"/bin/true", Cmd: []string{"/bin/true", ""},
[]string{""}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -199,11 +192,9 @@ func TestExitCode(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
falseContainer, err := runtime.Create( falseContainer, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"/bin/false", Cmd: []string{"/bin/false", ""},
[]string{""}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -228,11 +219,9 @@ func TestRestart(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"echo", Cmd: []string{"echo", "-n", "foobar"},
[]string{"-n", "foobar"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -262,11 +251,9 @@ func TestRestartStdin(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"cat", Cmd: []string{"cat"},
[]string{},
GetTestImage(runtime).Id,
&Config{
OpenStdin: true, OpenStdin: true,
}, },
) )
@ -313,11 +300,9 @@ func TestUser(t *testing.T) {
defer nuke(runtime) defer nuke(runtime)
// Default user must be root // Default user must be root
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"id", Cmd: []string{"id"},
[]string{}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -332,11 +317,9 @@ func TestUser(t *testing.T) {
} }
// Set a username // Set a username
container, err = runtime.Create( container, err = runtime.Create(GetTestImage(runtime).Id, &Config{
"id", Cmd: []string{"id"},
[]string{},
GetTestImage(runtime).Id,
&Config{
User: "root", User: "root",
}, },
) )
@ -353,11 +336,9 @@ func TestUser(t *testing.T) {
} }
// Set a UID // Set a UID
container, err = runtime.Create( container, err = runtime.Create(GetTestImage(runtime).Id, &Config{
"id", Cmd: []string{"id"},
[]string{},
GetTestImage(runtime).Id,
&Config{
User: "0", User: "0",
}, },
) )
@ -374,11 +355,9 @@ func TestUser(t *testing.T) {
} }
// Set a different user by uid // Set a different user by uid
container, err = runtime.Create( container, err = runtime.Create(GetTestImage(runtime).Id, &Config{
"id", Cmd: []string{"id"},
[]string{},
GetTestImage(runtime).Id,
&Config{
User: "1", User: "1",
}, },
) )
@ -397,11 +376,9 @@ func TestUser(t *testing.T) {
} }
// Set a different user by username // Set a different user by username
container, err = runtime.Create( container, err = runtime.Create(GetTestImage(runtime).Id, &Config{
"id", Cmd: []string{"id"},
[]string{},
GetTestImage(runtime).Id,
&Config{
User: "daemon", User: "daemon",
}, },
) )
@ -425,22 +402,18 @@ func TestMultipleContainers(t *testing.T) {
} }
defer nuke(runtime) defer nuke(runtime)
container1, err := runtime.Create( container1, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"cat", Cmd: []string{"cat", "/dev/zero"},
[]string{"/dev/zero"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer runtime.Destroy(container1) defer runtime.Destroy(container1)
container2, err := runtime.Create( container2, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"cat", Cmd: []string{"cat", "/dev/zero"},
[]string{"/dev/zero"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -479,11 +452,9 @@ func TestStdin(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"cat", Cmd: []string{"cat"},
[]string{},
GetTestImage(runtime).Id,
&Config{
OpenStdin: true, OpenStdin: true,
}, },
) )
@ -514,11 +485,9 @@ func TestTty(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"cat", Cmd: []string{"cat"},
[]string{},
GetTestImage(runtime).Id,
&Config{
OpenStdin: true, OpenStdin: true,
}, },
) )
@ -549,11 +518,9 @@ func TestEnv(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"/usr/bin/env", Cmd: []string{"/usr/bin/env"},
[]string{}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -623,11 +590,9 @@ func TestLXCConfig(t *testing.T) {
memMin := 33554432 memMin := 33554432
memMax := 536870912 memMax := 536870912
mem := memMin + rand.Intn(memMax-memMin) mem := memMin + rand.Intn(memMax-memMin)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"/bin/true", Cmd: []string{"/bin/true"},
[]string{},
GetTestImage(runtime).Id,
&Config{
Hostname: "foobar", Hostname: "foobar",
Memory: int64(mem), Memory: int64(mem),
}, },
@ -651,11 +616,9 @@ func BenchmarkRunSequencial(b *testing.B) {
} }
defer nuke(runtime) defer nuke(runtime)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"echo", Cmd: []string{"echo", "-n", "foo"},
[]string{"-n", "foo"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
@ -687,11 +650,9 @@ func BenchmarkRunParallel(b *testing.B) {
complete := make(chan error) complete := make(chan error)
tasks = append(tasks, complete) tasks = append(tasks, complete)
go func(i int, complete chan error) { go func(i int, complete chan error) {
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"echo", Cmd: []string{"echo", "-n", "foo"},
[]string{"-n", "foo"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
complete <- err complete <- err

View File

@ -64,7 +64,7 @@ func (runtime *Runtime) containerRoot(id string) string {
return path.Join(runtime.repository, id) 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 // Lookup image
img, err := runtime.repositories.LookupImage(image) img, err := runtime.repositories.LookupImage(image)
if err != nil { if err != nil {
@ -74,11 +74,10 @@ func (runtime *Runtime) Create(command string, args []string, image string, conf
// FIXME: we should generate the ID here instead of receiving it as an argument // FIXME: we should generate the ID here instead of receiving it as an argument
Id: GenerateId(), Id: GenerateId(),
Created: time.Now(), Created: time.Now(),
Path: command, Path: config.Cmd[0],
Args: args, Args: config.Cmd[1:], //FIXME: de-duplicate from config
Config: config, Config: config,
Image: img.Id, // Always use the resolved image id Image: img.Id, // Always use the resolved image id
//FIXME: store the name under which the image was given, for reference
NetworkSettings: &NetworkSettings{}, NetworkSettings: &NetworkSettings{},
// FIXME: do we need to store this in the container? // FIXME: do we need to store this in the container?
SysInitPath: sysInitPath, SysInitPath: sysInitPath,

View File

@ -112,11 +112,9 @@ func TestRuntimeCreate(t *testing.T) {
if len(runtime.List()) != 0 { if len(runtime.List()) != 0 {
t.Errorf("Expected 0 containers, %v found", len(runtime.List())) t.Errorf("Expected 0 containers, %v found", len(runtime.List()))
} }
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"ls", Cmd: []string{"ls", "-al"},
[]string{"-al"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -160,11 +158,9 @@ func TestDestroy(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container, err := runtime.Create( container, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"ls", Cmd: []string{"ls", "-al"},
[]string{"-al"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -208,33 +204,27 @@ func TestGet(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
defer nuke(runtime) defer nuke(runtime)
container1, err := runtime.Create( container1, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"ls", Cmd: []string{"ls", "-al"},
[]string{"-al"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer runtime.Destroy(container1) defer runtime.Destroy(container1)
container2, err := runtime.Create( container2, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"ls", Cmd: []string{"ls", "-al"},
[]string{"-al"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
defer runtime.Destroy(container2) defer runtime.Destroy(container2)
container3, err := runtime.Create( container3, err := runtime.Create(GetTestImage(runtime).Id, &Config{
"ls", Cmd: []string{"ls", "-al"},
[]string{"-al"}, },
GetTestImage(runtime).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -274,11 +264,9 @@ func TestRestore(t *testing.T) {
} }
// Create a container with one instance of docker // Create a container with one instance of docker
container1, err := runtime1.Create( container1, err := runtime1.Create(GetTestImage(runtime1).Id, &Config{
"ls", Cmd: []string{"ls", "-al"},
[]string{"-al"}, },
GetTestImage(runtime1).Id,
&Config{},
) )
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)