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 6e04c8de8d..0000000000
--- a/builder.go
+++ /dev/null
@@ -1,181 +0,0 @@
-package docker
-
-import (
- "fmt"
- "github.com/dotcloud/docker/utils"
- "io/ioutil"
- "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: if hostname, build hostname and hosts files
- container.HostnamePath = path.Join(container.root, "hostname")
- ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
-
- hostsContent := []byte(`
-127.0.0.1 localhost
-::1 localhost ip6-localhost ip6-loopback
-fe00::0 ip6-localnet
-ff00::0 ip6-mcastprefix
-ff02::1 ip6-allnodes
-ff02::2 ip6-allrouters
-`)
-
- container.HostsPath = path.Join(container.root, "hosts")
-
- if container.Config.Domainname != "" {
- hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
- hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
- } else {
- hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s\n", container.Config.Hostname)), hostsContent...)
- hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s\n", container.Config.Hostname)), hostsContent...)
- }
-
- ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
-
- // Step 4: 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/commands.go b/commands.go
index 656790624e..df6feac93b 100644
--- a/commands.go
+++ b/commands.go
@@ -367,16 +367,11 @@ func (cli *DockerCli) CmdWait(args ...string) error {
return nil
}
for _, name := range cmd.Args() {
- body, _, err := cli.call("POST", "/containers/"+name+"/wait", nil)
+ status, err := waitForExit(cli, name)
if err != nil {
fmt.Fprintf(cli.err, "%s", err)
} else {
- var out APIWait
- err = json.Unmarshal(body, &out)
- if err != nil {
- return err
- }
- fmt.Fprintf(cli.out, "%d\n", out.StatusCode)
+ fmt.Fprintf(cli.out, "%d\n", status)
}
}
return nil
@@ -1477,8 +1472,18 @@ func (cli *DockerCli) CmdRun(args ...string) error {
}
if !config.AttachStdout && !config.AttachStderr {
+ // Detached mode
<-wait
+ } else {
+ status, err := waitForExit(cli, runResult.ID)
+ if err != nil {
+ return err
+ }
+ if status != 0 {
+ return &utils.StatusError{status}
+ }
}
+
return nil
}
@@ -1759,6 +1764,19 @@ func (cli *DockerCli) LoadConfigFile() (err error) {
return err
}
+func waitForExit(cli *DockerCli, containerId string) (int, error) {
+ body, _, err := cli.call("POST", "/containers/"+containerId+"/wait", nil)
+ if err != nil {
+ return -1, err
+ }
+
+ var out APIWait
+ if err := json.Unmarshal(body, &out); err != nil {
+ return -1, err
+ }
+ return out.StatusCode, nil
+}
+
func NewDockerCli(in io.ReadCloser, out, err io.Writer, proto, addr string) *DockerCli {
var (
isTerminal = false
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/docker/docker.go b/docker/docker.go
index 9bbd40b696..a0021f3a87 100644
--- a/docker/docker.go
+++ b/docker/docker.go
@@ -75,6 +75,9 @@ func main() {
}
protoAddrParts := strings.SplitN(flHosts[0], "://", 2)
if err := docker.ParseCommands(protoAddrParts[0], protoAddrParts[1], flag.Args()...); err != nil {
+ if sterr, ok := err.(*utils.StatusError); ok {
+ os.Exit(sterr.Status)
+ }
log.Fatal(err)
os.Exit(-1)
}
diff --git a/docs/sources/api/docker_remote_api.rst b/docs/sources/api/docker_remote_api.rst
index be15c3494e..0563247dc4 100644
--- a/docs/sources/api/docker_remote_api.rst
+++ b/docs/sources/api/docker_remote_api.rst
@@ -175,8 +175,6 @@ and we will add the libraries here.
+======================+================+============================================+
| Python | docker-py | https://github.com/dotcloud/docker-py |
+----------------------+----------------+--------------------------------------------+
-| Ruby | docker-ruby | https://github.com/ActiveState/docker-ruby |
-+----------------------+----------------+--------------------------------------------+
| Ruby | docker-client | https://github.com/geku/docker-client |
+----------------------+----------------+--------------------------------------------+
| Ruby | docker-api | https://github.com/swipely/docker-api |
@@ -193,3 +191,5 @@ and we will add the libraries here.
+----------------------+----------------+--------------------------------------------+
| Erlang | erldocker | https://github.com/proger/erldocker |
+----------------------+----------------+--------------------------------------------+
+| Go | go-dockerclient| https://github.com/fsouza/go-dockerclient |
++----------------------+----------------+--------------------------------------------+
diff --git a/docs/sources/examples/index.rst b/docs/sources/examples/index.rst
index 2664b95e54..1114790280 100644
--- a/docs/sources/examples/index.rst
+++ b/docs/sources/examples/index.rst
@@ -22,3 +22,4 @@ Contents:
couchdb_data_volumes
postgresql_service
mongodb
+ running_riak_service
diff --git a/docs/sources/examples/running_riak_service.rst b/docs/sources/examples/running_riak_service.rst
new file mode 100644
index 0000000000..d98de6a77c
--- /dev/null
+++ b/docs/sources/examples/running_riak_service.rst
@@ -0,0 +1,151 @@
+:title: Running a Riak service
+:description: Build a Docker image with Riak pre-installed
+:keywords: docker, example, package installation, networking, riak
+
+Riak Service
+==============================
+
+.. include:: example_header.inc
+
+The goal of this example is to show you how to build a Docker image with Riak
+pre-installed.
+
+Creating a ``Dockerfile``
++++++++++++++++++++++++++
+
+Create an empty file called ``Dockerfile``:
+
+.. code-block:: bash
+
+ touch Dockerfile
+
+Next, define the parent image you want to use to build your image on top of.
+We’ll use `Ubuntu `_ (tag: ``latest``),
+which is available on the `docker index `_:
+
+.. code-block:: bash
+
+ # Riak
+ #
+ # VERSION 0.1.0
+
+ # Use the Ubuntu base image provided by dotCloud
+ FROM ubuntu:latest
+ MAINTAINER Hector Castro hector@basho.com
+
+Next, we update the APT cache and apply any updates:
+
+.. code-block:: bash
+
+ # Update the APT cache
+ RUN sed -i.bak 's/main$/main universe/' /etc/apt/sources.list
+ RUN apt-get update
+ RUN apt-get upgrade -y
+
+After that, we install and setup a few dependencies:
+
+- ``curl`` is used to download Basho's APT repository key
+- ``lsb-release`` helps us derive the Ubuntu release codename
+- ``openssh-server`` allows us to login to containers remotely and join Riak
+ nodes to form a cluster
+- ``supervisor`` is used manage the OpenSSH and Riak processes
+
+.. code-block:: bash
+
+ # Install and setup project dependencies
+ RUN apt-get install -y curl lsb-release supervisor openssh-server
+
+ RUN mkdir -p /var/run/sshd
+ RUN mkdir -p /var/log/supervisor
+
+ RUN locale-gen en_US en_US.UTF-8
+
+ ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
+
+ RUN echo 'root:basho' | chpasswd
+
+Next, we add Basho's APT repository:
+
+.. code-block:: bash
+
+ RUN curl -s http://apt.basho.com/gpg/basho.apt.key | apt-key add --
+ RUN echo "deb http://apt.basho.com $(lsb_release -cs) main" > /etc/apt/sources.list.d/basho.list
+ RUN apt-get update
+
+After that, we install Riak and alter a few defaults:
+
+.. code-block:: bash
+
+ # Install Riak and prepare it to run
+ RUN apt-get install -y riak
+ RUN sed -i.bak 's/127.0.0.1/0.0.0.0/' /etc/riak/app.config
+ RUN echo "ulimit -n 4096" >> /etc/default/riak
+
+Almost there. Next, we add a hack to get us by the lack of ``initctl``:
+
+.. code-block:: bash
+
+ # Hack for initctl
+ # See: https://github.com/dotcloud/docker/issues/1024
+ RUN dpkg-divert --local --rename --add /sbin/initctl
+ RUN ln -s /bin/true /sbin/initctl
+
+Then, we expose the Riak Protocol Buffers and HTTP interfaces, along with SSH:
+
+.. code-block:: bash
+
+ # Expose Riak Protocol Buffers and HTTP interfaces, along with SSH
+ EXPOSE 8087 8098 22
+
+Finally, run ``supervisord`` so that Riak and OpenSSH are started:
+
+.. code-block:: bash
+
+ CMD ["/usr/bin/supervisord"]
+
+Create a ``supervisord`` configuration file
++++++++++++++++++++++++++++++++++++++++++++
+
+Create an empty file called ``supervisord.conf``. Make sure it's at the same
+level as your ``Dockerfile``:
+
+.. code-block:: bash
+
+ touch supervisord.conf
+
+Populate it with the following program definitions:
+
+.. code-block:: bash
+
+ [supervisord]
+ nodaemon=true
+
+ [program:sshd]
+ command=/usr/sbin/sshd -D
+ stdout_logfile=/var/log/supervisor/%(program_name)s.log
+ stderr_logfile=/var/log/supervisor/%(program_name)s.log
+ autorestart=true
+
+ [program:riak]
+ command=bash -c ". /etc/default/riak && /usr/sbin/riak console"
+ pidfile=/var/log/riak/riak.pid
+ stdout_logfile=/var/log/supervisor/%(program_name)s.log
+ stderr_logfile=/var/log/supervisor/%(program_name)s.log
+
+Build the Docker image for Riak
++++++++++++++++++++++++++++++++
+
+Now you should be able to build a Docker image for Riak:
+
+.. code-block:: bash
+
+ docker build -t "/riak" .
+
+Next steps
+++++++++++
+
+Riak is a distributed database. Many production deployments consist of `at
+least five nodes `_. See the `docker-riak `_ project details on how to deploy a Riak cluster using Docker and
+Pipework.
diff --git a/docs/sources/installation/gentoolinux.rst b/docs/sources/installation/gentoolinux.rst
index aa809768ee..6e41e049d1 100644
--- a/docs/sources/installation/gentoolinux.rst
+++ b/docs/sources/installation/gentoolinux.rst
@@ -11,7 +11,10 @@ Gentoo Linux
.. include:: install_unofficial.inc
-Installing Docker on Gentoo Linux can be accomplished by using the overlay provided at https://github.com/tianon/docker-overlay. The most up-to-date documentation for properly installing the overlay can be found in the overlay README. The information here is provided for reference, and may be out of date.
+Installing Docker on Gentoo Linux can be accomplished by using the overlay
+provided at https://github.com/tianon/docker-overlay. The most up-to-date
+documentation for properly installing the overlay can be found in the overlay
+README. The information here is provided for reference, and may be out of date.
Installation
^^^^^^^^^^^^
@@ -22,30 +25,49 @@ Ensure that layman is installed:
sudo emerge -av app-portage/layman
-Using your favorite editor, add ``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the ``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the `Gentoo Wiki `_), then invoke the following:
+Using your favorite editor, add
+``https://raw.github.com/tianon/docker-overlay/master/repositories.xml`` to the
+``overlays`` section in ``/etc/layman/layman.cfg`` (as per instructions on the
+`Gentoo Wiki `_),
+then invoke the following:
.. code-block:: bash
sudo layman -f -a docker
-Once that completes, the ``app-emulation/lxc-docker`` package will be available for emerge:
+Once that completes, the ``app-emulation/docker`` package will be available
+for emerge:
.. code-block:: bash
- sudo emerge -av app-emulation/lxc-docker
+ sudo emerge -av app-emulation/docker
-If you prefer to use the official binaries, or just do not wish to compile docker, emerge ``app-emulation/lxc-docker-bin`` instead. It is important to remember that Gentoo is still an unsupported platform, even when using the official binaries.
+If you prefer to use the official binaries, or just do not wish to compile
+docker, emerge ``app-emulation/docker-bin`` instead. It is important to
+remember that Gentoo is still an unsupported platform, even when using the
+official binaries.
-The package should already include all the necessary dependencies. For the simplest installation experience, use ``sys-kernel/aufs-sources`` directly as your kernel sources. If you prefer not to use ``sys-kernel/aufs-sources``, the portage tree also contains ``sys-fs/aufs3``, which contains the patches necessary for adding AUFS support to other kernel source packages (and a ``kernel-patch`` use flag to perform the patching automatically).
+The package should already include all the necessary dependencies. For the
+simplest installation experience, use ``sys-kernel/aufs-sources`` directly as
+your kernel sources. If you prefer not to use ``sys-kernel/aufs-sources``, the
+portage tree also contains ``sys-fs/aufs3``, which contains the patches
+necessary for adding AUFS support to other kernel source packages (and a
+``kernel-patch`` use flag to perform the patching automatically).
-Between ``app-emulation/lxc`` and ``app-emulation/lxc-docker``, all the necessary kernel configuration flags should be checked for and warned about in the standard manner.
+Between ``app-emulation/lxc`` and ``app-emulation/docker``, all the
+necessary kernel configuration flags should be checked for and warned about in
+the standard manner.
-If any issues arise from this ebuild or the resulting binary, including and especially missing kernel configuration flags and/or dependencies, `open an issue `_ on the docker-overlay repository or ping tianon in the #docker IRC channel.
+If any issues arise from this ebuild or the resulting binary, including and
+especially missing kernel configuration flags and/or dependencies, `open an
+issue `_ on the docker-overlay
+repository or ping tianon in the #docker IRC channel.
Starting Docker
^^^^^^^^^^^^^^^
-Ensure that you are running a kernel that includes the necessary AUFS support and includes all the necessary modules and/or configuration for LXC.
+Ensure that you are running a kernel that includes the necessary AUFS support
+and includes all the necessary modules and/or configuration for LXC.
OpenRC
------
@@ -80,7 +102,8 @@ To start on system boot:
Network Configuration
^^^^^^^^^^^^^^^^^^^^^
-IPv4 packet forwarding is disabled by default, so internet access from inside the container will not work unless ``net.ipv4.ip_forward`` is enabled:
+IPv4 packet forwarding is disabled by default, so internet access from inside
+the container will not work unless ``net.ipv4.ip_forward`` is enabled:
.. code-block:: bash
@@ -95,4 +118,8 @@ Or, to enable it more permanently:
fork/exec /usr/sbin/lxc-start: operation not permitted
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-Unfortunately, Gentoo suffers from `issue #1422 `_, meaning that after every fresh start of docker, the first docker run fails due to some tricky terminal issues, so be sure to run something trivial (such as ``docker run -i -t busybox echo hi``) before attempting to run anything important.
+Unfortunately, Gentoo suffers from `issue #1422
+`_, meaning that after every
+fresh start of docker, the first docker run fails due to some tricky terminal
+issues, so be sure to run something trivial (such as ``docker run -i -t busybox
+echo hi``) before attempting to run anything important.
diff --git a/runtime.go b/runtime.go
index 002c0fe10a..aff1773fdf 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,159 @@ 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: if hostname, build hostname and hosts files
+ container.HostnamePath = path.Join(container.root, "hostname")
+ ioutil.WriteFile(container.HostnamePath, []byte(container.Config.Hostname+"\n"), 0644)
+
+ hostsContent := []byte(`
+127.0.0.1 localhost
+::1 localhost ip6-localhost ip6-loopback
+fe00::0 ip6-localnet
+ff00::0 ip6-mcastprefix
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+`)
+
+ container.HostsPath = path.Join(container.root, "hosts")
+
+ if container.Config.Domainname != "" {
+ hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
+ hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s.%s %s\n", container.Config.Hostname, container.Config.Domainname, container.Config.Hostname)), hostsContent...)
+ } else {
+ hostsContent = append([]byte(fmt.Sprintf("::1\t\t%s\n", container.Config.Hostname)), hostsContent...)
+ hostsContent = append([]byte(fmt.Sprintf("127.0.0.1\t%s\n", container.Config.Hostname)), hostsContent...)
+ }
+
+ ioutil.WriteFile(container.HostsPath, hostsContent, 0644)
+
+ // Step 4: 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 +491,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 {
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/utils.go b/utils/utils.go
index aa34abdddc..d417690c0c 100644
--- a/utils/utils.go
+++ b/utils/utils.go
@@ -1012,3 +1012,12 @@ func (graph *DependencyGraph) GenerateTraversalMap() ([][]string, error) {
}
return result, nil
}
+
+// An StatusError reports an unsuccessful exit by a command.
+type StatusError struct {
+ Status int
+}
+
+func (e *StatusError) Error() string {
+ return fmt.Sprintf("Status: %d", e.Status)
+}
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
}