Explorar o código

rebase master

Victor Vieux %!s(int64=12) %!d(string=hai) anos
pai
achega
bd847f66c6

+ 14 - 25
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"},

+ 0 - 181
builder.go

@@ -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
-}

+ 4 - 6
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{},

+ 25 - 7
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

+ 32 - 38
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)
 	}

+ 3 - 0
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)
 		}

+ 2 - 2
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  |
++----------------------+----------------+--------------------------------------------+

+ 1 - 0
docs/sources/examples/index.rst

@@ -22,3 +22,4 @@ Contents:
    couchdb_data_volumes
    postgresql_service
    mongodb
+   running_riak_service

+ 151 - 0
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 <https://index.docker.io/_/ubuntu/>`_ (tag: ``latest``),
+which is available on the `docker index <http://index.docker.io>`_:
+
+.. 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 "<yourname>/riak" .
+
+Next steps
+++++++++++
+
+Riak is a distributed database. Many production deployments consist of `at
+least five nodes <http://basho.com/why-your-riak-cluster-should-have-at-least-
+five-nodes/>`_. See the `docker-riak <https://github.com/hectcastro /docker-
+riak>`_ project details on how to deploy a Riak cluster using Docker and
+Pipework.

+ 38 - 11
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 <http://wiki.gentoo.org/wiki/Layman#Adding_custom_overlays>`_), 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 <http://wiki.gentoo.org/wiki/Layman#Adding_custom_overlays>`_),
+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 <https://github.com/tianon/docker-overlay/issues>`_ 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 <https://github.com/tianon/docker-overlay/issues>`_ 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 <https://github.com/dotcloud/docker/issues/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
+<https://github.com/dotcloud/docker/issues/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.

+ 168 - 0
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 <nil>")
@@ -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 {

+ 5 - 7
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)},

+ 4 - 6
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) {
 

+ 9 - 0
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)
+}

+ 1 - 1
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
 	}