Przeglądaj źródła

Merge pull request #12457 from calavera/remove_engine_job_builder

Remove engine.Job from Builder.
Jessie Frazelle 10 lat temu
rodzic
commit
82f75df9e9
8 zmienionych plików z 281 dodań i 230 usunięć
  1. 20 0
      api/server/form.go
  2. 55 0
      api/server/form_test.go
  3. 77 63
      api/server/server.go
  4. 109 85
      builder/job.go
  5. 1 49
      daemon/commit.go
  6. 0 5
      docker/daemon.go
  7. 10 28
      graph/import.go
  8. 9 0
      utils/utils.go

+ 20 - 0
api/server/form.go

@@ -0,0 +1,20 @@
+package server
+
+import (
+	"net/http"
+	"strconv"
+	"strings"
+)
+
+func boolValue(r *http.Request, k string) bool {
+	s := strings.ToLower(strings.TrimSpace(r.FormValue(k)))
+	return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
+}
+
+func int64Value(r *http.Request, k string) int64 {
+	val, err := strconv.ParseInt(r.FormValue(k), 10, 64)
+	if err != nil {
+		return 0
+	}
+	return val
+}

+ 55 - 0
api/server/form_test.go

@@ -0,0 +1,55 @@
+package server
+
+import (
+	"net/http"
+	"net/url"
+	"testing"
+)
+
+func TestBoolValue(t *testing.T) {
+	cases := map[string]bool{
+		"":      false,
+		"0":     false,
+		"no":    false,
+		"false": false,
+		"none":  false,
+		"1":     true,
+		"yes":   true,
+		"true":  true,
+		"one":   true,
+		"100":   true,
+	}
+
+	for c, e := range cases {
+		v := url.Values{}
+		v.Set("test", c)
+		r, _ := http.NewRequest("POST", "", nil)
+		r.Form = v
+
+		a := boolValue(r, "test")
+		if a != e {
+			t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
+		}
+	}
+}
+
+func TestInt64Value(t *testing.T) {
+	cases := map[string]int64{
+		"":     0,
+		"asdf": 0,
+		"0":    0,
+		"1":    1,
+	}
+
+	for c, e := range cases {
+		v := url.Values{}
+		v.Set("test", c)
+		r, _ := http.NewRequest("POST", "", nil)
+		r.Form = v
+
+		a := int64Value(r, "test")
+		if a != e {
+			t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
+		}
+	}
+}

+ 77 - 63
api/server/server.go

@@ -23,6 +23,7 @@ import (
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/autogen/dockerversion"
+	"github.com/docker/docker/builder"
 	"github.com/docker/docker/daemon"
 	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/engine"
@@ -238,12 +239,12 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
 	return json.NewEncoder(w).Encode(v)
 }
 
-func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
+func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) {
 	w.Header().Set("Content-Type", "application/json")
 	if flush {
-		job.Stdout.Add(utils.NewWriteFlusher(w))
+		out.Add(utils.NewWriteFlusher(w))
 	} else {
-		job.Stdout.Add(w)
+		out.Add(w)
 	}
 }
 
@@ -382,7 +383,7 @@ func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w ht
 		Filters: r.Form.Get("filters"),
 		// FIXME this parameter could just be a match filter
 		Filter: r.Form.Get("filter"),
-		All:    toBool(r.Form.Get("all")),
+		All:    boolValue(r, "all"),
 	}
 
 	images, err := s.daemon.Repositories().Images(&imagesConfig)
@@ -598,8 +599,8 @@ func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version,
 	}
 
 	config := &daemon.ContainersConfig{
-		All:     toBool(r.Form.Get("all")),
-		Size:    toBool(r.Form.Get("size")),
+		All:     boolValue(r, "all"),
+		Size:    boolValue(r, "size"),
 		Since:   r.Form.Get("since"),
 		Before:  r.Form.Get("before"),
 		Filters: r.Form.Get("filters"),
@@ -641,14 +642,14 @@ func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version,
 	}
 
 	// Validate args here, because we can't return not StatusOK after job.Run() call
-	stdout, stderr := toBool(r.Form.Get("stdout")), toBool(r.Form.Get("stderr"))
+	stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
 	if !(stdout || stderr) {
 		return fmt.Errorf("Bad parameters: you must choose at least one stream")
 	}
 
 	logsConfig := &daemon.ContainerLogsConfig{
-		Follow:     toBool(r.Form.Get("follow")),
-		Timestamps: toBool(r.Form.Get("timestamps")),
+		Follow:     boolValue(r, "follow"),
+		Timestamps: boolValue(r, "timestamps"),
 		Tail:       r.Form.Get("tail"),
 		UseStdout:  stdout,
 		UseStderr:  stderr,
@@ -672,7 +673,7 @@ func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w ht
 
 	repo := r.Form.Get("repo")
 	tag := r.Form.Get("tag")
-	force := toBool(r.Form.Get("force"))
+	force := boolValue(r, "force")
 	if err := s.daemon.Repositories().Tag(repo, tag, vars["name"], force); err != nil {
 		return err
 	}
@@ -691,11 +692,20 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.
 
 	cont := r.Form.Get("container")
 
-	pause := toBool(r.Form.Get("pause"))
+	pause := boolValue(r, "pause")
 	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
 		pause = true
 	}
 
+	c, _, err := runconfig.DecodeContainerConfig(r.Body)
+	if err != nil && err != io.EOF { //Do not fail if body is empty.
+		return err
+	}
+
+	if c == nil {
+		c = &runconfig.Config{}
+	}
+
 	containerCommitConfig := &daemon.ContainerCommitConfig{
 		Pause:   pause,
 		Repo:    r.Form.Get("repo"),
@@ -703,10 +713,10 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.
 		Author:  r.Form.Get("author"),
 		Comment: r.Form.Get("comment"),
 		Changes: r.Form["changes"],
-		Config:  r.Body,
+		Config:  c,
 	}
 
-	imgID, err := s.daemon.ContainerCommit(cont, containerCommitConfig)
+	imgID, err := builder.Commit(s.daemon, eng, cont, containerCommitConfig)
 	if err != nil {
 		return err
 	}
@@ -783,10 +793,15 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w
 			imageImportConfig.Json = false
 		}
 
-		if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig, eng); err != nil {
+		newConfig, err := builder.BuildFromConfig(s.daemon, eng, &runconfig.Config{}, imageImportConfig.Changes)
+		if err != nil {
 			return err
 		}
+		imageImportConfig.ContainerConfig = newConfig
 
+		if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig); err != nil {
+			return err
+		}
 	}
 
 	return nil
@@ -859,7 +874,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
 	job.Setenv("tag", r.Form.Get("tag"))
 	if version.GreaterThan("1.0") {
 		job.SetenvBool("json", true)
-		streamJSON(job, w, true)
+		streamJSON(job.Stdout, w, true)
 	} else {
 		job.Stdout.Add(utils.NewWriteFlusher(w))
 	}
@@ -978,9 +993,9 @@ func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w
 
 	name := vars["name"]
 	config := &daemon.ContainerRmConfig{
-		ForceRemove:  toBool(r.Form.Get("force")),
-		RemoveVolume: toBool(r.Form.Get("v")),
-		RemoveLink:   toBool(r.Form.Get("link")),
+		ForceRemove:  boolValue(r, "force"),
+		RemoveVolume: boolValue(r, "v"),
+		RemoveLink:   boolValue(r, "link"),
 	}
 
 	if err := s.daemon.ContainerRm(name, config); err != nil {
@@ -1005,8 +1020,8 @@ func (s *Server) deleteImages(eng *engine.Engine, version version.Version, w htt
 	}
 
 	name := vars["name"]
-	force := toBool(r.Form.Get("force"))
-	noprune := toBool(r.Form.Get("noprune"))
+	force := boolValue(r, "force")
+	noprune := boolValue(r, "noprune")
 
 	list, err := s.daemon.ImageDelete(name, force, noprune)
 	if err != nil {
@@ -1153,19 +1168,19 @@ func (s *Server) postContainersAttach(eng *engine.Engine, version version.Versio
 	} else {
 		errStream = outStream
 	}
-	logs := toBool(r.Form.Get("logs"))
-	stream := toBool(r.Form.Get("stream"))
+	logs := boolValue(r, "logs")
+	stream := boolValue(r, "stream")
 
 	var stdin io.ReadCloser
 	var stdout, stderr io.Writer
 
-	if toBool(r.Form.Get("stdin")) {
+	if boolValue(r, "stdin") {
 		stdin = inStream
 	}
-	if toBool(r.Form.Get("stdout")) {
+	if boolValue(r, "stdout") {
 		stdout = outStream
 	}
-	if toBool(r.Form.Get("stderr")) {
+	if boolValue(r, "stderr") {
 		stderr = errStream
 	}
 
@@ -1209,7 +1224,7 @@ func (s *Server) getContainersByName(eng *engine.Engine, version version.Version
 	if version.LessThan("1.12") {
 		job.SetenvBool("raw", true)
 	}
-	streamJSON(job, w, false)
+	streamJSON(job.Stdout, w, false)
 	return job.Run()
 }
 
@@ -1234,7 +1249,7 @@ func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w
 	if version.LessThan("1.12") {
 		job.SetenvBool("raw", true)
 	}
-	streamJSON(job, w, false)
+	streamJSON(job.Stdout, w, false)
 	return job.Run()
 }
 
@@ -1247,7 +1262,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
 		authConfig        = &registry.AuthConfig{}
 		configFileEncoded = r.Header.Get("X-Registry-Config")
 		configFile        = &registry.ConfigFile{}
-		job               = eng.Job("build")
+		buildConfig       = builder.NewBuildConfig()
 	)
 
 	// This block can be removed when API versions prior to 1.9 are deprecated.
@@ -1273,36 +1288,38 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
 	}
 
 	if version.GreaterThanOrEqualTo("1.8") {
-		job.SetenvBool("json", true)
-		streamJSON(job, w, true)
-	} else {
-		job.Stdout.Add(utils.NewWriteFlusher(w))
+		w.Header().Set("Content-Type", "application/json")
+		buildConfig.JSONFormat = true
 	}
 
-	if toBool(r.FormValue("forcerm")) && version.GreaterThanOrEqualTo("1.12") {
-		job.Setenv("rm", "1")
+	if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
+		buildConfig.Remove = true
 	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
-		job.Setenv("rm", "1")
+		buildConfig.Remove = true
 	} else {
-		job.Setenv("rm", r.FormValue("rm"))
-	}
-	if toBool(r.FormValue("pull")) && version.GreaterThanOrEqualTo("1.16") {
-		job.Setenv("pull", "1")
-	}
-	job.Stdin.Add(r.Body)
-	job.Setenv("remote", r.FormValue("remote"))
-	job.Setenv("dockerfile", r.FormValue("dockerfile"))
-	job.Setenv("t", r.FormValue("t"))
-	job.Setenv("q", r.FormValue("q"))
-	job.Setenv("nocache", r.FormValue("nocache"))
-	job.Setenv("forcerm", r.FormValue("forcerm"))
-	job.SetenvJson("authConfig", authConfig)
-	job.SetenvJson("configFile", configFile)
-	job.Setenv("memswap", r.FormValue("memswap"))
-	job.Setenv("memory", r.FormValue("memory"))
-	job.Setenv("cpusetcpus", r.FormValue("cpusetcpus"))
-	job.Setenv("cpusetmems", r.FormValue("cpusetmems"))
-	job.Setenv("cpushares", r.FormValue("cpushares"))
+		buildConfig.Remove = boolValue(r, "rm")
+	}
+	if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
+		buildConfig.Pull = true
+	}
+
+	output := utils.NewWriteFlusher(w)
+	buildConfig.Stdout = output
+	buildConfig.Context = r.Body
+
+	buildConfig.RemoteURL = r.FormValue("remote")
+	buildConfig.DockerfileName = r.FormValue("dockerfile")
+	buildConfig.RepoName = r.FormValue("t")
+	buildConfig.SuppressOutput = boolValue(r, "q")
+	buildConfig.NoCache = boolValue(r, "nocache")
+	buildConfig.ForceRemove = boolValue(r, "forcerm")
+	buildConfig.AuthConfig = authConfig
+	buildConfig.ConfigFile = configFile
+	buildConfig.MemorySwap = int64Value(r, "memswap")
+	buildConfig.Memory = int64Value(r, "memory")
+	buildConfig.CpuShares = int64Value(r, "cpushares")
+	buildConfig.CpuSetCpus = r.FormValue("cpusetcpus")
+	buildConfig.CpuSetMems = r.FormValue("cpusetmems")
 
 	// Job cancellation. Note: not all job types support this.
 	if closeNotifier, ok := w.(http.CloseNotifier); ok {
@@ -1312,14 +1329,16 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
 			select {
 			case <-finished:
 			case <-closeNotifier.CloseNotify():
-				logrus.Infof("Client disconnected, cancelling job: %s", job.Name)
-				job.Cancel()
+				logrus.Infof("Client disconnected, cancelling job: build")
+				buildConfig.Cancel()
 			}
 		}()
 	}
 
-	if err := job.Run(); err != nil {
-		if !job.Stdout.Used() {
+	if err := builder.Build(s.daemon, eng, buildConfig); err != nil {
+		// Do not write the error in the http output if it's still empty.
+		// This prevents from writing a 200(OK) when there is an interal error.
+		if !output.Flushed() {
 			return err
 		}
 		sf := streamformatter.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
@@ -1673,8 +1692,3 @@ func allocateDaemonPort(addr string) error {
 	}
 	return nil
 }
-
-func toBool(s string) bool {
-	s = strings.ToLower(strings.TrimSpace(s))
-	return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
-}

+ 109 - 85
builder/job.go

@@ -2,13 +2,13 @@ package builder
 
 import (
 	"bytes"
-	"encoding/json"
 	"fmt"
 	"io"
 	"io/ioutil"
 	"os"
 	"os/exec"
 	"strings"
+	"sync"
 
 	"github.com/docker/docker/api"
 	"github.com/docker/docker/builder/parser"
@@ -36,44 +36,61 @@ var validCommitCommands = map[string]bool{
 	"onbuild":    true,
 }
 
-type BuilderJob struct {
-	Engine *engine.Engine
-	Daemon *daemon.Daemon
+type Config struct {
+	DockerfileName string
+	RemoteURL      string
+	RepoName       string
+	SuppressOutput bool
+	NoCache        bool
+	Remove         bool
+	ForceRemove    bool
+	Pull           bool
+	JSONFormat     bool
+	Memory         int64
+	MemorySwap     int64
+	CpuShares      int64
+	CpuSetCpus     string
+	CpuSetMems     string
+	AuthConfig     *registry.AuthConfig
+	ConfigFile     *registry.ConfigFile
+
+	Stdout  io.Writer
+	Context io.ReadCloser
+	// When closed, the job has been cancelled.
+	// Note: not all jobs implement cancellation.
+	// See Job.Cancel() and Job.WaitCancelled()
+	cancelled  chan struct{}
+	cancelOnce sync.Once
 }
 
-func (b *BuilderJob) Install() {
-	b.Engine.Register("build", b.CmdBuild)
-	b.Engine.Register("build_config", b.CmdBuildConfig)
+// When called, causes the Job.WaitCancelled channel to unblock.
+func (b *Config) Cancel() {
+	b.cancelOnce.Do(func() {
+		close(b.cancelled)
+	})
 }
 
-func (b *BuilderJob) CmdBuild(job *engine.Job) error {
-	if len(job.Args) != 0 {
-		return fmt.Errorf("Usage: %s\n", job.Name)
+// Returns a channel which is closed ("never blocks") when the job is cancelled.
+func (b *Config) WaitCancelled() <-chan struct{} {
+	return b.cancelled
+}
+
+func NewBuildConfig() *Config {
+	return &Config{
+		AuthConfig: &registry.AuthConfig{},
+		ConfigFile: &registry.ConfigFile{},
+		cancelled:  make(chan struct{}),
 	}
+}
+
+func Build(d *daemon.Daemon, e *engine.Engine, buildConfig *Config) error {
 	var (
-		dockerfileName = job.Getenv("dockerfile")
-		remoteURL      = job.Getenv("remote")
-		repoName       = job.Getenv("t")
-		suppressOutput = job.GetenvBool("q")
-		noCache        = job.GetenvBool("nocache")
-		rm             = job.GetenvBool("rm")
-		forceRm        = job.GetenvBool("forcerm")
-		pull           = job.GetenvBool("pull")
-		memory         = job.GetenvInt64("memory")
-		memorySwap     = job.GetenvInt64("memswap")
-		cpuShares      = job.GetenvInt64("cpushares")
-		cpuSetCpus     = job.Getenv("cpusetcpus")
-		cpuSetMems     = job.Getenv("cpusetmems")
-		authConfig     = &registry.AuthConfig{}
-		configFile     = &registry.ConfigFile{}
-		tag            string
-		context        io.ReadCloser
+		repoName string
+		tag      string
+		context  io.ReadCloser
 	)
 
-	job.GetenvJson("authConfig", authConfig)
-	job.GetenvJson("configFile", configFile)
-
-	repoName, tag = parsers.ParseRepositoryTag(repoName)
+	repoName, tag = parsers.ParseRepositoryTag(buildConfig.RepoName)
 	if repoName != "" {
 		if err := registry.ValidateRepositoryName(repoName); err != nil {
 			return err
@@ -85,11 +102,11 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
 		}
 	}
 
-	if remoteURL == "" {
-		context = ioutil.NopCloser(job.Stdin)
-	} else if urlutil.IsGitURL(remoteURL) {
-		if !urlutil.IsGitTransport(remoteURL) {
-			remoteURL = "https://" + remoteURL
+	if buildConfig.RemoteURL == "" {
+		context = ioutil.NopCloser(buildConfig.Context)
+	} else if urlutil.IsGitURL(buildConfig.RemoteURL) {
+		if !urlutil.IsGitTransport(buildConfig.RemoteURL) {
+			buildConfig.RemoteURL = "https://" + buildConfig.RemoteURL
 		}
 		root, err := ioutil.TempDir("", "docker-build-git")
 		if err != nil {
@@ -97,7 +114,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
 		}
 		defer os.RemoveAll(root)
 
-		if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
+		if output, err := exec.Command("git", "clone", "--recursive", buildConfig.RemoteURL, root).CombinedOutput(); err != nil {
 			return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
 		}
 
@@ -106,8 +123,8 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
 			return err
 		}
 		context = c
-	} else if urlutil.IsURL(remoteURL) {
-		f, err := httputils.Download(remoteURL)
+	} else if urlutil.IsURL(buildConfig.RemoteURL) {
+		f, err := httputils.Download(buildConfig.RemoteURL)
 		if err != nil {
 			return err
 		}
@@ -119,9 +136,9 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
 
 		// When we're downloading just a Dockerfile put it in
 		// the default name - don't allow the client to move/specify it
-		dockerfileName = api.DefaultDockerfileName
+		buildConfig.DockerfileName = api.DefaultDockerfileName
 
-		c, err := archive.Generate(dockerfileName, string(dockerFile))
+		c, err := archive.Generate(buildConfig.DockerfileName, string(dockerFile))
 		if err != nil {
 			return err
 		}
@@ -129,35 +146,35 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
 	}
 	defer context.Close()
 
-	sf := streamformatter.NewStreamFormatter(job.GetenvBool("json"))
+	sf := streamformatter.NewStreamFormatter(buildConfig.JSONFormat)
 
 	builder := &Builder{
-		Daemon: b.Daemon,
-		Engine: b.Engine,
+		Daemon: d,
+		Engine: e,
 		OutStream: &streamformatter.StdoutFormater{
-			Writer:          job.Stdout,
+			Writer:          buildConfig.Stdout,
 			StreamFormatter: sf,
 		},
 		ErrStream: &streamformatter.StderrFormater{
-			Writer:          job.Stdout,
+			Writer:          buildConfig.Stdout,
 			StreamFormatter: sf,
 		},
-		Verbose:         !suppressOutput,
-		UtilizeCache:    !noCache,
-		Remove:          rm,
-		ForceRemove:     forceRm,
-		Pull:            pull,
-		OutOld:          job.Stdout,
+		Verbose:         !buildConfig.SuppressOutput,
+		UtilizeCache:    !buildConfig.NoCache,
+		Remove:          buildConfig.Remove,
+		ForceRemove:     buildConfig.ForceRemove,
+		Pull:            buildConfig.Pull,
+		OutOld:          buildConfig.Stdout,
 		StreamFormatter: sf,
-		AuthConfig:      authConfig,
-		ConfigFile:      configFile,
-		dockerfileName:  dockerfileName,
-		cpuShares:       cpuShares,
-		cpuSetCpus:      cpuSetCpus,
-		cpuSetMems:      cpuSetMems,
-		memory:          memory,
-		memorySwap:      memorySwap,
-		cancelled:       job.WaitCancelled(),
+		AuthConfig:      buildConfig.AuthConfig,
+		ConfigFile:      buildConfig.ConfigFile,
+		dockerfileName:  buildConfig.DockerfileName,
+		cpuShares:       buildConfig.CpuShares,
+		cpuSetCpus:      buildConfig.CpuSetCpus,
+		cpuSetMems:      buildConfig.CpuSetMems,
+		memory:          buildConfig.Memory,
+		memorySwap:      buildConfig.MemorySwap,
+		cancelled:       buildConfig.WaitCancelled(),
 	}
 
 	id, err := builder.Run(context)
@@ -166,41 +183,28 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
 	}
 
 	if repoName != "" {
-		b.Daemon.Repositories().Tag(repoName, tag, id, true)
+		return d.Repositories().Tag(repoName, tag, id, true)
 	}
 	return nil
 }
 
-func (b *BuilderJob) CmdBuildConfig(job *engine.Job) error {
-	if len(job.Args) != 0 {
-		return fmt.Errorf("Usage: %s\n", job.Name)
-	}
-
-	var (
-		changes   = job.GetenvList("changes")
-		newConfig runconfig.Config
-	)
-
-	if err := job.GetenvJson("config", &newConfig); err != nil {
-		return err
-	}
-
+func BuildFromConfig(d *daemon.Daemon, e *engine.Engine, c *runconfig.Config, changes []string) (*runconfig.Config, error) {
 	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
 	if err != nil {
-		return err
+		return nil, err
 	}
 
 	// ensure that the commands are valid
 	for _, n := range ast.Children {
 		if !validCommitCommands[n.Value] {
-			return fmt.Errorf("%s is not a valid change command", n.Value)
+			return nil, fmt.Errorf("%s is not a valid change command", n.Value)
 		}
 	}
 
 	builder := &Builder{
-		Daemon:        b.Daemon,
-		Engine:        b.Engine,
-		Config:        &newConfig,
+		Daemon:        d,
+		Engine:        e,
+		Config:        c,
 		OutStream:     ioutil.Discard,
 		ErrStream:     ioutil.Discard,
 		disableCommit: true,
@@ -208,12 +212,32 @@ func (b *BuilderJob) CmdBuildConfig(job *engine.Job) error {
 
 	for i, n := range ast.Children {
 		if err := builder.dispatch(i, n); err != nil {
-			return err
+			return nil, err
 		}
 	}
 
-	if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil {
-		return err
+	return builder.Config, nil
+}
+
+func Commit(d *daemon.Daemon, eng *engine.Engine, name string, c *daemon.ContainerCommitConfig) (string, error) {
+	container, err := d.Get(name)
+	if err != nil {
+		return "", err
 	}
-	return nil
+
+	newConfig, err := BuildFromConfig(d, eng, c.Config, c.Changes)
+	if err != nil {
+		return "", err
+	}
+
+	if err := runconfig.Merge(newConfig, container.Config); err != nil {
+		return "", err
+	}
+
+	img, err := d.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, newConfig)
+	if err != nil {
+		return "", err
+	}
+
+	return img.ID, nil
 }

+ 1 - 49
daemon/commit.go

@@ -1,12 +1,6 @@
 package daemon
 
 import (
-	"bytes"
-	"encoding/json"
-	"io"
-
-	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/engine"
 	"github.com/docker/docker/image"
 	"github.com/docker/docker/runconfig"
 )
@@ -18,49 +12,7 @@ type ContainerCommitConfig struct {
 	Author  string
 	Comment string
 	Changes []string
-	Config  io.ReadCloser
-}
-
-func (daemon *Daemon) ContainerCommit(name string, c *ContainerCommitConfig) (string, error) {
-	container, err := daemon.Get(name)
-	if err != nil {
-		return "", err
-	}
-
-	var (
-		subenv       engine.Env
-		config       = container.Config
-		stdoutBuffer = bytes.NewBuffer(nil)
-		newConfig    runconfig.Config
-	)
-
-	if err := subenv.Decode(c.Config); err != nil {
-		logrus.Errorf("%s", err)
-	}
-
-	buildConfigJob := daemon.eng.Job("build_config")
-	buildConfigJob.Stdout.Add(stdoutBuffer)
-	buildConfigJob.SetenvList("changes", c.Changes)
-	// FIXME this should be remove when we remove deprecated config param
-	buildConfigJob.SetenvSubEnv("config", &subenv)
-
-	if err := buildConfigJob.Run(); err != nil {
-		return "", err
-	}
-	if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil {
-		return "", err
-	}
-
-	if err := runconfig.Merge(&newConfig, config); err != nil {
-		return "", err
-	}
-
-	img, err := daemon.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, &newConfig)
-	if err != nil {
-		return "", err
-	}
-
-	return img.ID, nil
+	Config  *runconfig.Config
 }
 
 // Commit creates a new filesystem image from the current state of a container.

+ 0 - 5
docker/daemon.go

@@ -11,7 +11,6 @@ import (
 	"github.com/Sirupsen/logrus"
 	apiserver "github.com/docker/docker/api/server"
 	"github.com/docker/docker/autogen/dockerversion"
-	"github.com/docker/docker/builder"
 	"github.com/docker/docker/daemon"
 	_ "github.com/docker/docker/daemon/execdriver/lxc"
 	_ "github.com/docker/docker/daemon/execdriver/native"
@@ -141,9 +140,6 @@ func mainDaemon() {
 		"graphdriver": d.GraphDriver().String(),
 	}).Info("Docker daemon")
 
-	b := &builder.BuilderJob{eng, d}
-	b.Install()
-
 	// after the daemon is done setting up we can tell the api to start
 	// accepting connections with specified daemon
 	api.AcceptConnections(d)
@@ -155,7 +151,6 @@ func mainDaemon() {
 	if errAPI != nil {
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
 	}
-
 }
 
 // currentUserIsOwner checks whether the current user is the owner of the given

+ 10 - 28
graph/import.go

@@ -1,13 +1,10 @@
 package graph
 
 import (
-	"bytes"
-	"encoding/json"
 	"io"
 	"net/http"
 	"net/url"
 
-	"github.com/docker/docker/engine"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/httputils"
 	"github.com/docker/docker/pkg/progressreader"
@@ -17,20 +14,18 @@ import (
 )
 
 type ImageImportConfig struct {
-	Changes   []string
-	InConfig  io.ReadCloser
-	Json      bool
-	OutStream io.Writer
-	//OutStream WriteFlusher
+	Changes         []string
+	InConfig        io.ReadCloser
+	Json            bool
+	OutStream       io.Writer
+	ContainerConfig *runconfig.Config
 }
 
-func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig, eng *engine.Engine) error {
+func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig) error {
 	var (
-		sf           = streamformatter.NewStreamFormatter(imageImportConfig.Json)
-		archive      archive.ArchiveReader
-		resp         *http.Response
-		stdoutBuffer = bytes.NewBuffer(nil)
-		newConfig    runconfig.Config
+		sf      = streamformatter.NewStreamFormatter(imageImportConfig.Json)
+		archive archive.ArchiveReader
+		resp    *http.Response
 	)
 
 	if src == "-" {
@@ -63,20 +58,7 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig
 		archive = progressReader
 	}
 
-	buildConfigJob := eng.Job("build_config")
-	buildConfigJob.Stdout.Add(stdoutBuffer)
-	buildConfigJob.SetenvList("changes", imageImportConfig.Changes)
-	// FIXME this should be remove when we remove deprecated config param
-	//buildConfigJob.Setenv("config", job.Getenv("config"))
-
-	if err := buildConfigJob.Run(); err != nil {
-		return err
-	}
-	if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil {
-		return err
-	}
-
-	img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, &newConfig)
+	img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, imageImportConfig.ContainerConfig)
 	if err != nil {
 		return err
 	}

+ 9 - 0
utils/utils.go

@@ -128,12 +128,14 @@ type WriteFlusher struct {
 	sync.Mutex
 	w       io.Writer
 	flusher http.Flusher
+	flushed bool
 }
 
 func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
 	wf.Lock()
 	defer wf.Unlock()
 	n, err = wf.w.Write(b)
+	wf.flushed = true
 	wf.flusher.Flush()
 	return n, err
 }
@@ -142,9 +144,16 @@ func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
 func (wf *WriteFlusher) Flush() {
 	wf.Lock()
 	defer wf.Unlock()
+	wf.flushed = true
 	wf.flusher.Flush()
 }
 
+func (wf *WriteFlusher) Flushed() bool {
+	wf.Lock()
+	defer wf.Unlock()
+	return wf.flushed
+}
+
 func NewWriteFlusher(w io.Writer) *WriteFlusher {
 	var flusher http.Flusher
 	if f, ok := w.(http.Flusher); ok {