diff --git a/api_test.go b/api_test.go index 1e8ed20b98..837a20c244 100644 --- a/api_test.go +++ b/api_test.go @@ -321,7 +321,7 @@ func TestGetContainersJSON(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "test"}, }) @@ -357,10 +357,8 @@ func TestGetContainersExport(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, @@ -409,10 +407,8 @@ func TestGetContainersChanges(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/rm", "/etc/passwd"}, @@ -458,9 +454,7 @@ func TestGetContainersTop(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "cat"}, @@ -541,10 +535,8 @@ func TestGetContainersByName(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "test"}, @@ -574,10 +566,9 @@ func TestPostCommit(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, @@ -671,7 +662,7 @@ func TestPostContainersKill(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -713,7 +704,7 @@ func TestPostContainersRestart(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -767,7 +758,7 @@ func TestPostContainersStart(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -817,7 +808,7 @@ func TestPostContainersStop(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -864,7 +855,7 @@ func TestPostContainersWait(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sleep", "1"}, @@ -906,7 +897,7 @@ func TestPostContainersAttach(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/cat"}, @@ -998,7 +989,7 @@ func TestDeleteContainers(t *testing.T) { srv := &Server{runtime: runtime} - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test"}, }) @@ -1185,10 +1176,8 @@ func TestPostContainersCopy(t *testing.T) { srv := &Server{runtime: runtime} - builder := NewBuilder(runtime) - // Create a container and remove a file - container, err := builder.Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"touch", "/test.txt"}, diff --git a/builder.go b/builder.go deleted file mode 100644 index 9124f76ac1..0000000000 --- a/builder.go +++ /dev/null @@ -1,154 +0,0 @@ -package docker - -import ( - "fmt" - "github.com/dotcloud/docker/utils" - "os" - "path" - "time" -) - -var defaultDns = []string{"8.8.8.8", "8.8.4.4"} - -type Builder struct { - runtime *Runtime - repositories *TagStore - graph *Graph - - config *Config - image *Image -} - -func NewBuilder(runtime *Runtime) *Builder { - return &Builder{ - runtime: runtime, - graph: runtime.graph, - repositories: runtime.repositories, - } -} - -func (builder *Builder) Create(config *Config) (*Container, error) { - // Lookup image - img, err := builder.repositories.LookupImage(config.Image) - if err != nil { - return nil, err - } - - if img.Config != nil { - MergeConfig(config, img.Config) - } - - if len(config.Entrypoint) != 0 && config.Cmd == nil { - config.Cmd = []string{} - } else if config.Cmd == nil || len(config.Cmd) == 0 { - return nil, fmt.Errorf("No command specified") - } - - // Generate id - id := GenerateID() - // Generate default hostname - // FIXME: the lxc template no longer needs to set a default hostname - if config.Hostname == "" { - config.Hostname = id[:12] - } - - var args []string - var entrypoint string - - if len(config.Entrypoint) != 0 { - entrypoint = config.Entrypoint[0] - args = append(config.Entrypoint[1:], config.Cmd...) - } else { - entrypoint = config.Cmd[0] - args = config.Cmd[1:] - } - - container := &Container{ - // FIXME: we should generate the ID here instead of receiving it as an argument - ID: id, - Created: time.Now(), - Path: entrypoint, - Args: args, //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, - } - container.root = builder.runtime.containerRoot(container.ID) - // Step 1: create the container directory. - // This doubles as a barrier to avoid race conditions. - if err := os.Mkdir(container.root, 0700); err != nil { - return nil, err - } - - resolvConf, err := utils.GetResolvConf() - if err != nil { - return nil, err - } - - if len(config.Dns) == 0 && len(builder.runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { - //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns - builder.runtime.Dns = defaultDns - } - - // If custom dns exists, then create a resolv.conf for the container - if len(config.Dns) > 0 || len(builder.runtime.Dns) > 0 { - var dns []string - if len(config.Dns) > 0 { - dns = config.Dns - } else { - dns = builder.runtime.Dns - } - container.ResolvConfPath = path.Join(container.root, "resolv.conf") - f, err := os.Create(container.ResolvConfPath) - if err != nil { - return nil, err - } - defer f.Close() - for _, dns := range dns { - if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil { - return nil, err - } - } - } else { - container.ResolvConfPath = "/etc/resolv.conf" - } - - // Step 2: save the container json - if err := container.ToDisk(); err != nil { - return nil, err - } - // Step 3: register the container - if err := builder.runtime.Register(container); err != nil { - return nil, err - } - return container, nil -} - -// Commit creates a new filesystem image from the current state of a container. -// The image can optionally be tagged into a repository -func (builder *Builder) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) { - // FIXME: freeze the container before copying it to avoid data corruption? - // FIXME: this shouldn't be in commands. - if err := container.EnsureMounted(); err != nil { - return nil, err - } - - rwTar, err := container.ExportRw() - if err != nil { - return nil, err - } - // Create a new image from the container's base layers + a new layer from container changes - img, err := builder.graph.Create(rwTar, container, comment, author, config) - if err != nil { - return nil, err - } - // Register the image if needed - if repository != "" { - if err := builder.repositories.Set(repository, tag, img.ID, true); err != nil { - return img, err - } - } - return img, nil -} diff --git a/buildfile.go b/buildfile.go index 4c8db2c60e..c3c212c693 100644 --- a/buildfile.go +++ b/buildfile.go @@ -23,7 +23,6 @@ type BuildFile interface { type buildFile struct { runtime *Runtime - builder *Builder srv *Server image string @@ -337,7 +336,7 @@ func (b *buildFile) CmdAdd(args string) error { b.config.Image = b.image // Create the container and start it - container, err := b.builder.Create(b.config) + container, err := b.runtime.Create(b.config) if err != nil { return err } @@ -372,7 +371,7 @@ func (b *buildFile) run() (string, error) { b.config.Image = b.image // Create the container and start it - c, err := b.builder.Create(b.config) + c, err := b.runtime.Create(b.config) if err != nil { return "", err } @@ -428,7 +427,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { } } - container, err := b.builder.Create(b.config) + container, err := b.runtime.Create(b.config) if err != nil { return err } @@ -450,7 +449,7 @@ func (b *buildFile) commit(id string, autoCmd []string, comment string) error { autoConfig := *b.config autoConfig.Cmd = autoCmd // Commit the container - image, err := b.builder.Commit(container, "", "", "", b.maintainer, &autoConfig) + image, err := b.runtime.Commit(container, "", "", "", b.maintainer, &autoConfig) if err != nil { return err } @@ -524,7 +523,6 @@ func (b *buildFile) Build(context io.Reader) (string, error) { func NewBuildFile(srv *Server, out io.Writer, verbose, utilizeCache bool) BuildFile { return &buildFile{ - builder: NewBuilder(srv.runtime), runtime: srv.runtime, srv: srv, config: &Config{}, diff --git a/container_test.go b/container_test.go index b06d531cf7..b792af76d5 100644 --- a/container_test.go +++ b/container_test.go @@ -18,7 +18,7 @@ import ( func TestIDFormat(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container1, err := NewBuilder(runtime).Create( + container1, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/sh", "-c", "echo hello world"}, @@ -388,7 +388,7 @@ func TestRun(t *testing.T) { func TestOutput(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, @@ -411,7 +411,7 @@ func TestKillDifferentUser(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, OpenStdin: true, @@ -471,7 +471,7 @@ func TestCreateVolume(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := NewBuilder(runtime).Create(config) + c, err := runtime.Create(config) if err != nil { t.Fatal(err) } @@ -486,7 +486,7 @@ func TestCreateVolume(t *testing.T) { func TestKill(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, @@ -530,9 +530,7 @@ func TestExitCode(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - - trueContainer, err := builder.Create(&Config{ + trueContainer, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true", ""}, }) @@ -547,7 +545,7 @@ func TestExitCode(t *testing.T) { t.Errorf("Unexpected exit code %d (expected 0)", trueContainer.State.ExitCode) } - falseContainer, err := builder.Create(&Config{ + falseContainer, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/false", ""}, }) @@ -566,7 +564,7 @@ func TestExitCode(t *testing.T) { func TestRestart(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, }, @@ -596,7 +594,7 @@ func TestRestart(t *testing.T) { func TestRestartStdin(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, @@ -673,10 +671,8 @@ func TestUser(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - // Default user must be root - container, err := builder.Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, }, @@ -694,7 +690,7 @@ func TestUser(t *testing.T) { } // Set a username - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -714,7 +710,7 @@ func TestUser(t *testing.T) { } // Set a UID - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -734,7 +730,7 @@ func TestUser(t *testing.T) { } // Set a different user by uid - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -756,7 +752,7 @@ func TestUser(t *testing.T) { } // Set a different user by username - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -776,7 +772,7 @@ func TestUser(t *testing.T) { } // Test an wrong username - container, err = builder.Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"id"}, @@ -797,9 +793,7 @@ func TestMultipleContainers(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - builder := NewBuilder(runtime) - - container1, err := builder.Create(&Config{ + container1, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, @@ -809,7 +803,7 @@ func TestMultipleContainers(t *testing.T) { } defer runtime.Destroy(container1) - container2, err := builder.Create(&Config{ + container2, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sleep", "2"}, }, @@ -853,7 +847,7 @@ func TestMultipleContainers(t *testing.T) { func TestStdin(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, @@ -898,7 +892,7 @@ func TestStdin(t *testing.T) { func TestTty(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat"}, @@ -943,7 +937,7 @@ func TestTty(t *testing.T) { func TestEnv(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"env"}, }, @@ -992,7 +986,7 @@ func TestEnv(t *testing.T) { func TestEntrypoint(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Entrypoint: []string{"/bin/echo"}, @@ -1015,7 +1009,7 @@ func TestEntrypoint(t *testing.T) { func TestEntrypointNoCmd(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Entrypoint: []string{"/bin/echo", "foobar"}, @@ -1066,7 +1060,7 @@ func TestLXCConfig(t *testing.T) { cpuMin := 100 cpuMax := 10000 cpu := cpuMin + rand.Intn(cpuMax-cpuMin) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true"}, @@ -1090,7 +1084,7 @@ func TestLXCConfig(t *testing.T) { func TestCustomLxcConfig(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/true"}, @@ -1121,7 +1115,7 @@ func BenchmarkRunSequencial(b *testing.B) { runtime := mkRuntime(b) defer nuke(runtime) for i := 0; i < b.N; i++ { - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foo"}, }, @@ -1153,7 +1147,7 @@ func BenchmarkRunParallel(b *testing.B) { complete := make(chan error) tasks = append(tasks, complete) go func(i int, complete chan error) { - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foo"}, }, @@ -1229,7 +1223,7 @@ func TestBindMounts(t *testing.T) { func TestVolumesFromReadonlyMount(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create( + container, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/echo", "-n", "foobar"}, @@ -1248,7 +1242,7 @@ func TestVolumesFromReadonlyMount(t *testing.T) { t.Fail() } - container2, err := NewBuilder(runtime).Create( + container2, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"/bin/echo", "-n", "foobar"}, @@ -1284,7 +1278,7 @@ func TestRestartWithVolumes(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"echo", "-n", "foobar"}, Volumes: map[string]struct{}{"/test": {}}, @@ -1327,7 +1321,7 @@ func TestVolumesFromWithVolumes(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sh", "-c", "echo -n bar > /test/foo"}, Volumes: map[string]struct{}{"/test": {}}, @@ -1354,7 +1348,7 @@ func TestVolumesFromWithVolumes(t *testing.T) { t.Fail() } - container2, err := NewBuilder(runtime).Create( + container2, err := runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"cat", "/test/foo"}, @@ -1395,7 +1389,7 @@ func TestOnlyLoopbackExistsWhenUsingDisableNetworkOption(t *testing.T) { if err != nil { t.Fatal(err) } - c, err := NewBuilder(runtime).Create(config) + c, err := runtime.Create(config) if err != nil { t.Fatal(err) } diff --git a/runtime.go b/runtime.go index 002c0fe10a..1ee7aa26f9 100644 --- a/runtime.go +++ b/runtime.go @@ -12,8 +12,11 @@ import ( "path" "sort" "strings" + "time" ) +var defaultDns = []string{"8.8.8.8", "8.8.4.4"} + type Capabilities struct { MemoryLimit bool SwapLimit bool @@ -42,6 +45,7 @@ func init() { sysInitPath = utils.SelfPath() } +// List returns an array of all containers registered in the runtime. func (runtime *Runtime) List() []*Container { containers := new(History) for e := runtime.containers.Front(); e != nil; e = e.Next() { @@ -60,6 +64,8 @@ func (runtime *Runtime) getContainerElement(id string) *list.Element { return nil } +// Get looks for a container by the specified ID or name, and returns it. +// If the container is not found, or if an error occurs, nil is returned. func (runtime *Runtime) Get(name string) *Container { id, err := runtime.idIndex.Get(name) if err != nil { @@ -72,6 +78,8 @@ func (runtime *Runtime) Get(name string) *Container { return e.Value.(*Container) } +// Exists returns a true if a container of the specified ID or name exists, +// false otherwise. func (runtime *Runtime) Exists(id string) bool { return runtime.Get(id) != nil } @@ -80,6 +88,9 @@ func (runtime *Runtime) containerRoot(id string) string { return path.Join(runtime.repository, id) } +// Load reads the contents of a container from disk and registers +// it with Register. +// This is typically done at startup. func (runtime *Runtime) Load(id string) (*Container, error) { container := &Container{root: runtime.containerRoot(id)} if err := container.FromDisk(); err != nil { @@ -177,6 +188,7 @@ func (runtime *Runtime) LogToDisk(src *utils.WriteBroadcaster, dst, stream strin return nil } +// Destroy unregisters a container from the runtime and cleanly removes its contents from the filesystem. func (runtime *Runtime) Destroy(container *Container) error { if container == nil { return fmt.Errorf("The given container is ") @@ -233,6 +245,7 @@ func (runtime *Runtime) restore() error { return nil } +// FIXME: comment please! func (runtime *Runtime) UpdateCapabilities(quiet bool) { if cgroupMemoryMountpoint, err := utils.FindCgroupMountpoint("memory"); err != nil { if !quiet { @@ -260,6 +273,133 @@ func (runtime *Runtime) UpdateCapabilities(quiet bool) { } } +// Create creates a new container from the given configuration. +func (runtime *Runtime) Create(config *Config) (*Container, error) { + // Lookup image + img, err := runtime.repositories.LookupImage(config.Image) + if err != nil { + return nil, err + } + + if img.Config != nil { + MergeConfig(config, img.Config) + } + + if len(config.Entrypoint) != 0 && config.Cmd == nil { + config.Cmd = []string{} + } else if config.Cmd == nil || len(config.Cmd) == 0 { + return nil, fmt.Errorf("No command specified") + } + + // Generate id + id := GenerateID() + // Generate default hostname + // FIXME: the lxc template no longer needs to set a default hostname + if config.Hostname == "" { + config.Hostname = id[:12] + } + + var args []string + var entrypoint string + + if len(config.Entrypoint) != 0 { + entrypoint = config.Entrypoint[0] + args = append(config.Entrypoint[1:], config.Cmd...) + } else { + entrypoint = config.Cmd[0] + args = config.Cmd[1:] + } + + container := &Container{ + // FIXME: we should generate the ID here instead of receiving it as an argument + ID: id, + Created: time.Now(), + Path: entrypoint, + Args: args, //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, + } + container.root = runtime.containerRoot(container.ID) + // Step 1: create the container directory. + // This doubles as a barrier to avoid race conditions. + if err := os.Mkdir(container.root, 0700); err != nil { + return nil, err + } + + resolvConf, err := utils.GetResolvConf() + if err != nil { + return nil, err + } + + if len(config.Dns) == 0 && len(runtime.Dns) == 0 && utils.CheckLocalDns(resolvConf) { + //"WARNING: Docker detected local DNS server on resolv.conf. Using default external servers: %v", defaultDns + runtime.Dns = defaultDns + } + + // If custom dns exists, then create a resolv.conf for the container + if len(config.Dns) > 0 || len(runtime.Dns) > 0 { + var dns []string + if len(config.Dns) > 0 { + dns = config.Dns + } else { + dns = runtime.Dns + } + container.ResolvConfPath = path.Join(container.root, "resolv.conf") + f, err := os.Create(container.ResolvConfPath) + if err != nil { + return nil, err + } + defer f.Close() + for _, dns := range dns { + if _, err := f.Write([]byte("nameserver " + dns + "\n")); err != nil { + return nil, err + } + } + } else { + container.ResolvConfPath = "/etc/resolv.conf" + } + + // Step 2: save the container json + if err := container.ToDisk(); err != nil { + return nil, err + } + // Step 3: register the container + if err := runtime.Register(container); err != nil { + return nil, err + } + return container, nil +} + +// Commit creates a new filesystem image from the current state of a container. +// The image can optionally be tagged into a repository +func (runtime *Runtime) Commit(container *Container, repository, tag, comment, author string, config *Config) (*Image, error) { + // FIXME: freeze the container before copying it to avoid data corruption? + // FIXME: this shouldn't be in commands. + if err := container.EnsureMounted(); err != nil { + return nil, err + } + + rwTar, err := container.ExportRw() + if err != nil { + return nil, err + } + // Create a new image from the container's base layers + a new layer from container changes + img, err := runtime.graph.Create(rwTar, container, comment, author, config) + if err != nil { + return nil, err + } + // Register the image if needed + if repository != "" { + if err := runtime.repositories.Set(repository, tag, img.ID, true); err != nil { + return img, err + } + } + return img, nil +} + // FIXME: harmonize with NewGraph() func NewRuntime(flGraphPath string, autoRestart bool, dns []string) (*Runtime, error) { runtime, err := NewRuntimeFromDirectory(flGraphPath, autoRestart) @@ -325,6 +465,8 @@ func NewRuntimeFromDirectory(root string, autoRestart bool) (*Runtime, error) { return runtime, nil } +// History is a convenience type for storing a list of containers, +// ordered by creation date. type History []*Container func (history *History) Len() int { @@ -347,3 +489,4 @@ func (history *History) Add(container *Container) { *history = append(*history, container) sort.Sort(history) } + diff --git a/runtime_test.go b/runtime_test.go index a65d962fa6..188e9c9076 100644 --- a/runtime_test.go +++ b/runtime_test.go @@ -144,9 +144,7 @@ func TestRuntimeCreate(t *testing.T) { t.Errorf("Expected 0 containers, %v found", len(runtime.List())) } - builder := NewBuilder(runtime) - - container, err := builder.Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}, }, @@ -187,7 +185,7 @@ func TestRuntimeCreate(t *testing.T) { } // Make sure crete with bad parameters returns an error - _, err = builder.Create( + _, err = runtime.Create( &Config{ Image: GetTestImage(runtime).ID, }, @@ -196,7 +194,7 @@ func TestRuntimeCreate(t *testing.T) { t.Fatal("Builder.Create should throw an error when Cmd is missing") } - _, err = builder.Create( + _, err = runtime.Create( &Config{ Image: GetTestImage(runtime).ID, Cmd: []string{}, @@ -210,7 +208,7 @@ func TestRuntimeCreate(t *testing.T) { func TestDestroy(t *testing.T) { runtime := mkRuntime(t) defer nuke(runtime) - container, err := NewBuilder(runtime).Create(&Config{ + container, err := runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"ls", "-al"}, }, @@ -296,7 +294,7 @@ func startEchoServerContainer(t *testing.T, proto string) (*Runtime, *Container, t.Fatal(fmt.Errorf("Unknown protocol %v", proto)) } t.Log("Trying port", strPort) - container, err = NewBuilder(runtime).Create(&Config{ + container, err = runtime.Create(&Config{ Image: GetTestImage(runtime).ID, Cmd: []string{"sh", "-c", cmd}, PortSpecs: []string{fmt.Sprintf("%s/%s", strPort, proto)}, diff --git a/server.go b/server.go index d2c4e5fa59..f662ad8a50 100644 --- a/server.go +++ b/server.go @@ -139,8 +139,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return "", err } - b := NewBuilder(srv.runtime) - c, err := b.Create(config) + c, err := srv.runtime.Create(config) if err != nil { return "", err } @@ -149,7 +148,7 @@ func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils. return "", err } // FIXME: Handle custom repo, tag comment, author - img, err = b.Commit(c, "", "", img.Comment, img.Author, nil) + img, err = srv.runtime.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { return "", err } @@ -400,7 +399,7 @@ func (srv *Server) ContainerCommit(name, repo, tag, author, comment string, conf if container == nil { return "", fmt.Errorf("No such container: %s", name) } - img, err := NewBuilder(srv.runtime).Commit(container, repo, tag, comment, author, config) + img, err := srv.runtime.Commit(container, repo, tag, comment, author, config) if err != nil { return "", err } @@ -915,8 +914,7 @@ func (srv *Server) ContainerCreate(config *Config) (string, error) { if config.Memory > 0 && !srv.runtime.capabilities.SwapLimit { config.MemorySwap = -1 } - b := NewBuilder(srv.runtime) - container, err := b.Create(config) + container, err := srv.runtime.Create(config) if err != nil { if srv.runtime.graph.IsNotExist(err) { diff --git a/utils_test.go b/utils_test.go index e8aae17186..740a5fc1bc 100644 --- a/utils_test.go +++ b/utils_test.go @@ -101,7 +101,7 @@ func mkContainer(r *Runtime, args []string, t *testing.T) (*Container, *HostConf if config.Image == "_" { config.Image = GetTestImage(r).ID } - c, err := NewBuilder(r).Create(config) + c, err := r.Create(config) if err != nil { return nil, nil, err }