Browse code

Merge pull request #12457 from calavera/remove_engine_job_builder

Remove engine.Job from Builder.

Jessie Frazelle authored on 2015/04/21 09:27:17
Showing 8 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,20 @@
0
+package server
1
+
2
+import (
3
+	"net/http"
4
+	"strconv"
5
+	"strings"
6
+)
7
+
8
+func boolValue(r *http.Request, k string) bool {
9
+	s := strings.ToLower(strings.TrimSpace(r.FormValue(k)))
10
+	return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
11
+}
12
+
13
+func int64Value(r *http.Request, k string) int64 {
14
+	val, err := strconv.ParseInt(r.FormValue(k), 10, 64)
15
+	if err != nil {
16
+		return 0
17
+	}
18
+	return val
19
+}
0 20
new file mode 100644
... ...
@@ -0,0 +1,55 @@
0
+package server
1
+
2
+import (
3
+	"net/http"
4
+	"net/url"
5
+	"testing"
6
+)
7
+
8
+func TestBoolValue(t *testing.T) {
9
+	cases := map[string]bool{
10
+		"":      false,
11
+		"0":     false,
12
+		"no":    false,
13
+		"false": false,
14
+		"none":  false,
15
+		"1":     true,
16
+		"yes":   true,
17
+		"true":  true,
18
+		"one":   true,
19
+		"100":   true,
20
+	}
21
+
22
+	for c, e := range cases {
23
+		v := url.Values{}
24
+		v.Set("test", c)
25
+		r, _ := http.NewRequest("POST", "", nil)
26
+		r.Form = v
27
+
28
+		a := boolValue(r, "test")
29
+		if a != e {
30
+			t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
31
+		}
32
+	}
33
+}
34
+
35
+func TestInt64Value(t *testing.T) {
36
+	cases := map[string]int64{
37
+		"":     0,
38
+		"asdf": 0,
39
+		"0":    0,
40
+		"1":    1,
41
+	}
42
+
43
+	for c, e := range cases {
44
+		v := url.Values{}
45
+		v.Set("test", c)
46
+		r, _ := http.NewRequest("POST", "", nil)
47
+		r.Form = v
48
+
49
+		a := int64Value(r, "test")
50
+		if a != e {
51
+			t.Fatalf("Value: %s, expected: %v, actual: %v", c, e, a)
52
+		}
53
+	}
54
+}
... ...
@@ -23,6 +23,7 @@ import (
23 23
 	"github.com/docker/docker/api"
24 24
 	"github.com/docker/docker/api/types"
25 25
 	"github.com/docker/docker/autogen/dockerversion"
26
+	"github.com/docker/docker/builder"
26 27
 	"github.com/docker/docker/daemon"
27 28
 	"github.com/docker/docker/daemon/networkdriver/bridge"
28 29
 	"github.com/docker/docker/engine"
... ...
@@ -238,12 +239,12 @@ func writeJSON(w http.ResponseWriter, code int, v interface{}) error {
238 238
 	return json.NewEncoder(w).Encode(v)
239 239
 }
240 240
 
241
-func streamJSON(job *engine.Job, w http.ResponseWriter, flush bool) {
241
+func streamJSON(out *engine.Output, w http.ResponseWriter, flush bool) {
242 242
 	w.Header().Set("Content-Type", "application/json")
243 243
 	if flush {
244
-		job.Stdout.Add(utils.NewWriteFlusher(w))
244
+		out.Add(utils.NewWriteFlusher(w))
245 245
 	} else {
246
-		job.Stdout.Add(w)
246
+		out.Add(w)
247 247
 	}
248 248
 }
249 249
 
... ...
@@ -382,7 +383,7 @@ func (s *Server) getImagesJSON(eng *engine.Engine, version version.Version, w ht
382 382
 		Filters: r.Form.Get("filters"),
383 383
 		// FIXME this parameter could just be a match filter
384 384
 		Filter: r.Form.Get("filter"),
385
-		All:    toBool(r.Form.Get("all")),
385
+		All:    boolValue(r, "all"),
386 386
 	}
387 387
 
388 388
 	images, err := s.daemon.Repositories().Images(&imagesConfig)
... ...
@@ -598,8 +599,8 @@ func (s *Server) getContainersJSON(eng *engine.Engine, version version.Version,
598 598
 	}
599 599
 
600 600
 	config := &daemon.ContainersConfig{
601
-		All:     toBool(r.Form.Get("all")),
602
-		Size:    toBool(r.Form.Get("size")),
601
+		All:     boolValue(r, "all"),
602
+		Size:    boolValue(r, "size"),
603 603
 		Since:   r.Form.Get("since"),
604 604
 		Before:  r.Form.Get("before"),
605 605
 		Filters: r.Form.Get("filters"),
... ...
@@ -641,14 +642,14 @@ func (s *Server) getContainersLogs(eng *engine.Engine, version version.Version,
641 641
 	}
642 642
 
643 643
 	// Validate args here, because we can't return not StatusOK after job.Run() call
644
-	stdout, stderr := toBool(r.Form.Get("stdout")), toBool(r.Form.Get("stderr"))
644
+	stdout, stderr := boolValue(r, "stdout"), boolValue(r, "stderr")
645 645
 	if !(stdout || stderr) {
646 646
 		return fmt.Errorf("Bad parameters: you must choose at least one stream")
647 647
 	}
648 648
 
649 649
 	logsConfig := &daemon.ContainerLogsConfig{
650
-		Follow:     toBool(r.Form.Get("follow")),
651
-		Timestamps: toBool(r.Form.Get("timestamps")),
650
+		Follow:     boolValue(r, "follow"),
651
+		Timestamps: boolValue(r, "timestamps"),
652 652
 		Tail:       r.Form.Get("tail"),
653 653
 		UseStdout:  stdout,
654 654
 		UseStderr:  stderr,
... ...
@@ -672,7 +673,7 @@ func (s *Server) postImagesTag(eng *engine.Engine, version version.Version, w ht
672 672
 
673 673
 	repo := r.Form.Get("repo")
674 674
 	tag := r.Form.Get("tag")
675
-	force := toBool(r.Form.Get("force"))
675
+	force := boolValue(r, "force")
676 676
 	if err := s.daemon.Repositories().Tag(repo, tag, vars["name"], force); err != nil {
677 677
 		return err
678 678
 	}
... ...
@@ -691,11 +692,20 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.
691 691
 
692 692
 	cont := r.Form.Get("container")
693 693
 
694
-	pause := toBool(r.Form.Get("pause"))
694
+	pause := boolValue(r, "pause")
695 695
 	if r.FormValue("pause") == "" && version.GreaterThanOrEqualTo("1.13") {
696 696
 		pause = true
697 697
 	}
698 698
 
699
+	c, _, err := runconfig.DecodeContainerConfig(r.Body)
700
+	if err != nil && err != io.EOF { //Do not fail if body is empty.
701
+		return err
702
+	}
703
+
704
+	if c == nil {
705
+		c = &runconfig.Config{}
706
+	}
707
+
699 708
 	containerCommitConfig := &daemon.ContainerCommitConfig{
700 709
 		Pause:   pause,
701 710
 		Repo:    r.Form.Get("repo"),
... ...
@@ -703,10 +713,10 @@ func (s *Server) postCommit(eng *engine.Engine, version version.Version, w http.
703 703
 		Author:  r.Form.Get("author"),
704 704
 		Comment: r.Form.Get("comment"),
705 705
 		Changes: r.Form["changes"],
706
-		Config:  r.Body,
706
+		Config:  c,
707 707
 	}
708 708
 
709
-	imgID, err := s.daemon.ContainerCommit(cont, containerCommitConfig)
709
+	imgID, err := builder.Commit(s.daemon, eng, cont, containerCommitConfig)
710 710
 	if err != nil {
711 711
 		return err
712 712
 	}
... ...
@@ -783,10 +793,15 @@ func (s *Server) postImagesCreate(eng *engine.Engine, version version.Version, w
783 783
 			imageImportConfig.Json = false
784 784
 		}
785 785
 
786
-		if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig, eng); err != nil {
786
+		newConfig, err := builder.BuildFromConfig(s.daemon, eng, &runconfig.Config{}, imageImportConfig.Changes)
787
+		if err != nil {
787 788
 			return err
788 789
 		}
790
+		imageImportConfig.ContainerConfig = newConfig
789 791
 
792
+		if err := s.daemon.Repositories().Import(src, repo, tag, imageImportConfig); err != nil {
793
+			return err
794
+		}
790 795
 	}
791 796
 
792 797
 	return nil
... ...
@@ -859,7 +874,7 @@ func (s *Server) postImagesPush(eng *engine.Engine, version version.Version, w h
859 859
 	job.Setenv("tag", r.Form.Get("tag"))
860 860
 	if version.GreaterThan("1.0") {
861 861
 		job.SetenvBool("json", true)
862
-		streamJSON(job, w, true)
862
+		streamJSON(job.Stdout, w, true)
863 863
 	} else {
864 864
 		job.Stdout.Add(utils.NewWriteFlusher(w))
865 865
 	}
... ...
@@ -978,9 +993,9 @@ func (s *Server) deleteContainers(eng *engine.Engine, version version.Version, w
978 978
 
979 979
 	name := vars["name"]
980 980
 	config := &daemon.ContainerRmConfig{
981
-		ForceRemove:  toBool(r.Form.Get("force")),
982
-		RemoveVolume: toBool(r.Form.Get("v")),
983
-		RemoveLink:   toBool(r.Form.Get("link")),
981
+		ForceRemove:  boolValue(r, "force"),
982
+		RemoveVolume: boolValue(r, "v"),
983
+		RemoveLink:   boolValue(r, "link"),
984 984
 	}
985 985
 
986 986
 	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
1005 1005
 	}
1006 1006
 
1007 1007
 	name := vars["name"]
1008
-	force := toBool(r.Form.Get("force"))
1009
-	noprune := toBool(r.Form.Get("noprune"))
1008
+	force := boolValue(r, "force")
1009
+	noprune := boolValue(r, "noprune")
1010 1010
 
1011 1011
 	list, err := s.daemon.ImageDelete(name, force, noprune)
1012 1012
 	if err != nil {
... ...
@@ -1153,19 +1168,19 @@ func (s *Server) postContainersAttach(eng *engine.Engine, version version.Versio
1153 1153
 	} else {
1154 1154
 		errStream = outStream
1155 1155
 	}
1156
-	logs := toBool(r.Form.Get("logs"))
1157
-	stream := toBool(r.Form.Get("stream"))
1156
+	logs := boolValue(r, "logs")
1157
+	stream := boolValue(r, "stream")
1158 1158
 
1159 1159
 	var stdin io.ReadCloser
1160 1160
 	var stdout, stderr io.Writer
1161 1161
 
1162
-	if toBool(r.Form.Get("stdin")) {
1162
+	if boolValue(r, "stdin") {
1163 1163
 		stdin = inStream
1164 1164
 	}
1165
-	if toBool(r.Form.Get("stdout")) {
1165
+	if boolValue(r, "stdout") {
1166 1166
 		stdout = outStream
1167 1167
 	}
1168
-	if toBool(r.Form.Get("stderr")) {
1168
+	if boolValue(r, "stderr") {
1169 1169
 		stderr = errStream
1170 1170
 	}
1171 1171
 
... ...
@@ -1209,7 +1224,7 @@ func (s *Server) getContainersByName(eng *engine.Engine, version version.Version
1209 1209
 	if version.LessThan("1.12") {
1210 1210
 		job.SetenvBool("raw", true)
1211 1211
 	}
1212
-	streamJSON(job, w, false)
1212
+	streamJSON(job.Stdout, w, false)
1213 1213
 	return job.Run()
1214 1214
 }
1215 1215
 
... ...
@@ -1234,7 +1249,7 @@ func (s *Server) getImagesByName(eng *engine.Engine, version version.Version, w
1234 1234
 	if version.LessThan("1.12") {
1235 1235
 		job.SetenvBool("raw", true)
1236 1236
 	}
1237
-	streamJSON(job, w, false)
1237
+	streamJSON(job.Stdout, w, false)
1238 1238
 	return job.Run()
1239 1239
 }
1240 1240
 
... ...
@@ -1247,7 +1262,7 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
1247 1247
 		authConfig        = &registry.AuthConfig{}
1248 1248
 		configFileEncoded = r.Header.Get("X-Registry-Config")
1249 1249
 		configFile        = &registry.ConfigFile{}
1250
-		job               = eng.Job("build")
1250
+		buildConfig       = builder.NewBuildConfig()
1251 1251
 	)
1252 1252
 
1253 1253
 	// 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
1273 1273
 	}
1274 1274
 
1275 1275
 	if version.GreaterThanOrEqualTo("1.8") {
1276
-		job.SetenvBool("json", true)
1277
-		streamJSON(job, w, true)
1278
-	} else {
1279
-		job.Stdout.Add(utils.NewWriteFlusher(w))
1276
+		w.Header().Set("Content-Type", "application/json")
1277
+		buildConfig.JSONFormat = true
1280 1278
 	}
1281 1279
 
1282
-	if toBool(r.FormValue("forcerm")) && version.GreaterThanOrEqualTo("1.12") {
1283
-		job.Setenv("rm", "1")
1280
+	if boolValue(r, "forcerm") && version.GreaterThanOrEqualTo("1.12") {
1281
+		buildConfig.Remove = true
1284 1282
 	} else if r.FormValue("rm") == "" && version.GreaterThanOrEqualTo("1.12") {
1285
-		job.Setenv("rm", "1")
1283
+		buildConfig.Remove = true
1286 1284
 	} else {
1287
-		job.Setenv("rm", r.FormValue("rm"))
1288
-	}
1289
-	if toBool(r.FormValue("pull")) && version.GreaterThanOrEqualTo("1.16") {
1290
-		job.Setenv("pull", "1")
1291
-	}
1292
-	job.Stdin.Add(r.Body)
1293
-	job.Setenv("remote", r.FormValue("remote"))
1294
-	job.Setenv("dockerfile", r.FormValue("dockerfile"))
1295
-	job.Setenv("t", r.FormValue("t"))
1296
-	job.Setenv("q", r.FormValue("q"))
1297
-	job.Setenv("nocache", r.FormValue("nocache"))
1298
-	job.Setenv("forcerm", r.FormValue("forcerm"))
1299
-	job.SetenvJson("authConfig", authConfig)
1300
-	job.SetenvJson("configFile", configFile)
1301
-	job.Setenv("memswap", r.FormValue("memswap"))
1302
-	job.Setenv("memory", r.FormValue("memory"))
1303
-	job.Setenv("cpusetcpus", r.FormValue("cpusetcpus"))
1304
-	job.Setenv("cpusetmems", r.FormValue("cpusetmems"))
1305
-	job.Setenv("cpushares", r.FormValue("cpushares"))
1285
+		buildConfig.Remove = boolValue(r, "rm")
1286
+	}
1287
+	if boolValue(r, "pull") && version.GreaterThanOrEqualTo("1.16") {
1288
+		buildConfig.Pull = true
1289
+	}
1290
+
1291
+	output := utils.NewWriteFlusher(w)
1292
+	buildConfig.Stdout = output
1293
+	buildConfig.Context = r.Body
1294
+
1295
+	buildConfig.RemoteURL = r.FormValue("remote")
1296
+	buildConfig.DockerfileName = r.FormValue("dockerfile")
1297
+	buildConfig.RepoName = r.FormValue("t")
1298
+	buildConfig.SuppressOutput = boolValue(r, "q")
1299
+	buildConfig.NoCache = boolValue(r, "nocache")
1300
+	buildConfig.ForceRemove = boolValue(r, "forcerm")
1301
+	buildConfig.AuthConfig = authConfig
1302
+	buildConfig.ConfigFile = configFile
1303
+	buildConfig.MemorySwap = int64Value(r, "memswap")
1304
+	buildConfig.Memory = int64Value(r, "memory")
1305
+	buildConfig.CpuShares = int64Value(r, "cpushares")
1306
+	buildConfig.CpuSetCpus = r.FormValue("cpusetcpus")
1307
+	buildConfig.CpuSetMems = r.FormValue("cpusetmems")
1306 1308
 
1307 1309
 	// Job cancellation. Note: not all job types support this.
1308 1310
 	if closeNotifier, ok := w.(http.CloseNotifier); ok {
... ...
@@ -1312,14 +1329,16 @@ func (s *Server) postBuild(eng *engine.Engine, version version.Version, w http.R
1312 1312
 			select {
1313 1313
 			case <-finished:
1314 1314
 			case <-closeNotifier.CloseNotify():
1315
-				logrus.Infof("Client disconnected, cancelling job: %s", job.Name)
1316
-				job.Cancel()
1315
+				logrus.Infof("Client disconnected, cancelling job: build")
1316
+				buildConfig.Cancel()
1317 1317
 			}
1318 1318
 		}()
1319 1319
 	}
1320 1320
 
1321
-	if err := job.Run(); err != nil {
1322
-		if !job.Stdout.Used() {
1321
+	if err := builder.Build(s.daemon, eng, buildConfig); err != nil {
1322
+		// Do not write the error in the http output if it's still empty.
1323
+		// This prevents from writing a 200(OK) when there is an interal error.
1324
+		if !output.Flushed() {
1323 1325
 			return err
1324 1326
 		}
1325 1327
 		sf := streamformatter.NewStreamFormatter(version.GreaterThanOrEqualTo("1.8"))
... ...
@@ -1673,8 +1692,3 @@ func allocateDaemonPort(addr string) error {
1673 1673
 	}
1674 1674
 	return nil
1675 1675
 }
1676
-
1677
-func toBool(s string) bool {
1678
-	s = strings.ToLower(strings.TrimSpace(s))
1679
-	return !(s == "" || s == "0" || s == "no" || s == "false" || s == "none")
1680
-}
... ...
@@ -2,13 +2,13 @@ package builder
2 2
 
3 3
 import (
4 4
 	"bytes"
5
-	"encoding/json"
6 5
 	"fmt"
7 6
 	"io"
8 7
 	"io/ioutil"
9 8
 	"os"
10 9
 	"os/exec"
11 10
 	"strings"
11
+	"sync"
12 12
 
13 13
 	"github.com/docker/docker/api"
14 14
 	"github.com/docker/docker/builder/parser"
... ...
@@ -36,44 +36,61 @@ var validCommitCommands = map[string]bool{
36 36
 	"onbuild":    true,
37 37
 }
38 38
 
39
-type BuilderJob struct {
40
-	Engine *engine.Engine
41
-	Daemon *daemon.Daemon
39
+type Config struct {
40
+	DockerfileName string
41
+	RemoteURL      string
42
+	RepoName       string
43
+	SuppressOutput bool
44
+	NoCache        bool
45
+	Remove         bool
46
+	ForceRemove    bool
47
+	Pull           bool
48
+	JSONFormat     bool
49
+	Memory         int64
50
+	MemorySwap     int64
51
+	CpuShares      int64
52
+	CpuSetCpus     string
53
+	CpuSetMems     string
54
+	AuthConfig     *registry.AuthConfig
55
+	ConfigFile     *registry.ConfigFile
56
+
57
+	Stdout  io.Writer
58
+	Context io.ReadCloser
59
+	// When closed, the job has been cancelled.
60
+	// Note: not all jobs implement cancellation.
61
+	// See Job.Cancel() and Job.WaitCancelled()
62
+	cancelled  chan struct{}
63
+	cancelOnce sync.Once
42 64
 }
43 65
 
44
-func (b *BuilderJob) Install() {
45
-	b.Engine.Register("build", b.CmdBuild)
46
-	b.Engine.Register("build_config", b.CmdBuildConfig)
66
+// When called, causes the Job.WaitCancelled channel to unblock.
67
+func (b *Config) Cancel() {
68
+	b.cancelOnce.Do(func() {
69
+		close(b.cancelled)
70
+	})
47 71
 }
48 72
 
49
-func (b *BuilderJob) CmdBuild(job *engine.Job) error {
50
-	if len(job.Args) != 0 {
51
-		return fmt.Errorf("Usage: %s\n", job.Name)
73
+// Returns a channel which is closed ("never blocks") when the job is cancelled.
74
+func (b *Config) WaitCancelled() <-chan struct{} {
75
+	return b.cancelled
76
+}
77
+
78
+func NewBuildConfig() *Config {
79
+	return &Config{
80
+		AuthConfig: &registry.AuthConfig{},
81
+		ConfigFile: &registry.ConfigFile{},
82
+		cancelled:  make(chan struct{}),
52 83
 	}
84
+}
85
+
86
+func Build(d *daemon.Daemon, e *engine.Engine, buildConfig *Config) error {
53 87
 	var (
54
-		dockerfileName = job.Getenv("dockerfile")
55
-		remoteURL      = job.Getenv("remote")
56
-		repoName       = job.Getenv("t")
57
-		suppressOutput = job.GetenvBool("q")
58
-		noCache        = job.GetenvBool("nocache")
59
-		rm             = job.GetenvBool("rm")
60
-		forceRm        = job.GetenvBool("forcerm")
61
-		pull           = job.GetenvBool("pull")
62
-		memory         = job.GetenvInt64("memory")
63
-		memorySwap     = job.GetenvInt64("memswap")
64
-		cpuShares      = job.GetenvInt64("cpushares")
65
-		cpuSetCpus     = job.Getenv("cpusetcpus")
66
-		cpuSetMems     = job.Getenv("cpusetmems")
67
-		authConfig     = &registry.AuthConfig{}
68
-		configFile     = &registry.ConfigFile{}
69
-		tag            string
70
-		context        io.ReadCloser
88
+		repoName string
89
+		tag      string
90
+		context  io.ReadCloser
71 91
 	)
72 92
 
73
-	job.GetenvJson("authConfig", authConfig)
74
-	job.GetenvJson("configFile", configFile)
75
-
76
-	repoName, tag = parsers.ParseRepositoryTag(repoName)
93
+	repoName, tag = parsers.ParseRepositoryTag(buildConfig.RepoName)
77 94
 	if repoName != "" {
78 95
 		if err := registry.ValidateRepositoryName(repoName); err != nil {
79 96
 			return err
... ...
@@ -85,11 +102,11 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
85 85
 		}
86 86
 	}
87 87
 
88
-	if remoteURL == "" {
89
-		context = ioutil.NopCloser(job.Stdin)
90
-	} else if urlutil.IsGitURL(remoteURL) {
91
-		if !urlutil.IsGitTransport(remoteURL) {
92
-			remoteURL = "https://" + remoteURL
88
+	if buildConfig.RemoteURL == "" {
89
+		context = ioutil.NopCloser(buildConfig.Context)
90
+	} else if urlutil.IsGitURL(buildConfig.RemoteURL) {
91
+		if !urlutil.IsGitTransport(buildConfig.RemoteURL) {
92
+			buildConfig.RemoteURL = "https://" + buildConfig.RemoteURL
93 93
 		}
94 94
 		root, err := ioutil.TempDir("", "docker-build-git")
95 95
 		if err != nil {
... ...
@@ -97,7 +114,7 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
97 97
 		}
98 98
 		defer os.RemoveAll(root)
99 99
 
100
-		if output, err := exec.Command("git", "clone", "--recursive", remoteURL, root).CombinedOutput(); err != nil {
100
+		if output, err := exec.Command("git", "clone", "--recursive", buildConfig.RemoteURL, root).CombinedOutput(); err != nil {
101 101
 			return fmt.Errorf("Error trying to use git: %s (%s)", err, output)
102 102
 		}
103 103
 
... ...
@@ -106,8 +123,8 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
106 106
 			return err
107 107
 		}
108 108
 		context = c
109
-	} else if urlutil.IsURL(remoteURL) {
110
-		f, err := httputils.Download(remoteURL)
109
+	} else if urlutil.IsURL(buildConfig.RemoteURL) {
110
+		f, err := httputils.Download(buildConfig.RemoteURL)
111 111
 		if err != nil {
112 112
 			return err
113 113
 		}
... ...
@@ -119,9 +136,9 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
119 119
 
120 120
 		// When we're downloading just a Dockerfile put it in
121 121
 		// the default name - don't allow the client to move/specify it
122
-		dockerfileName = api.DefaultDockerfileName
122
+		buildConfig.DockerfileName = api.DefaultDockerfileName
123 123
 
124
-		c, err := archive.Generate(dockerfileName, string(dockerFile))
124
+		c, err := archive.Generate(buildConfig.DockerfileName, string(dockerFile))
125 125
 		if err != nil {
126 126
 			return err
127 127
 		}
... ...
@@ -129,35 +146,35 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
129 129
 	}
130 130
 	defer context.Close()
131 131
 
132
-	sf := streamformatter.NewStreamFormatter(job.GetenvBool("json"))
132
+	sf := streamformatter.NewStreamFormatter(buildConfig.JSONFormat)
133 133
 
134 134
 	builder := &Builder{
135
-		Daemon: b.Daemon,
136
-		Engine: b.Engine,
135
+		Daemon: d,
136
+		Engine: e,
137 137
 		OutStream: &streamformatter.StdoutFormater{
138
-			Writer:          job.Stdout,
138
+			Writer:          buildConfig.Stdout,
139 139
 			StreamFormatter: sf,
140 140
 		},
141 141
 		ErrStream: &streamformatter.StderrFormater{
142
-			Writer:          job.Stdout,
142
+			Writer:          buildConfig.Stdout,
143 143
 			StreamFormatter: sf,
144 144
 		},
145
-		Verbose:         !suppressOutput,
146
-		UtilizeCache:    !noCache,
147
-		Remove:          rm,
148
-		ForceRemove:     forceRm,
149
-		Pull:            pull,
150
-		OutOld:          job.Stdout,
145
+		Verbose:         !buildConfig.SuppressOutput,
146
+		UtilizeCache:    !buildConfig.NoCache,
147
+		Remove:          buildConfig.Remove,
148
+		ForceRemove:     buildConfig.ForceRemove,
149
+		Pull:            buildConfig.Pull,
150
+		OutOld:          buildConfig.Stdout,
151 151
 		StreamFormatter: sf,
152
-		AuthConfig:      authConfig,
153
-		ConfigFile:      configFile,
154
-		dockerfileName:  dockerfileName,
155
-		cpuShares:       cpuShares,
156
-		cpuSetCpus:      cpuSetCpus,
157
-		cpuSetMems:      cpuSetMems,
158
-		memory:          memory,
159
-		memorySwap:      memorySwap,
160
-		cancelled:       job.WaitCancelled(),
152
+		AuthConfig:      buildConfig.AuthConfig,
153
+		ConfigFile:      buildConfig.ConfigFile,
154
+		dockerfileName:  buildConfig.DockerfileName,
155
+		cpuShares:       buildConfig.CpuShares,
156
+		cpuSetCpus:      buildConfig.CpuSetCpus,
157
+		cpuSetMems:      buildConfig.CpuSetMems,
158
+		memory:          buildConfig.Memory,
159
+		memorySwap:      buildConfig.MemorySwap,
160
+		cancelled:       buildConfig.WaitCancelled(),
161 161
 	}
162 162
 
163 163
 	id, err := builder.Run(context)
... ...
@@ -166,41 +183,28 @@ func (b *BuilderJob) CmdBuild(job *engine.Job) error {
166 166
 	}
167 167
 
168 168
 	if repoName != "" {
169
-		b.Daemon.Repositories().Tag(repoName, tag, id, true)
169
+		return d.Repositories().Tag(repoName, tag, id, true)
170 170
 	}
171 171
 	return nil
172 172
 }
173 173
 
174
-func (b *BuilderJob) CmdBuildConfig(job *engine.Job) error {
175
-	if len(job.Args) != 0 {
176
-		return fmt.Errorf("Usage: %s\n", job.Name)
177
-	}
178
-
179
-	var (
180
-		changes   = job.GetenvList("changes")
181
-		newConfig runconfig.Config
182
-	)
183
-
184
-	if err := job.GetenvJson("config", &newConfig); err != nil {
185
-		return err
186
-	}
187
-
174
+func BuildFromConfig(d *daemon.Daemon, e *engine.Engine, c *runconfig.Config, changes []string) (*runconfig.Config, error) {
188 175
 	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
189 176
 	if err != nil {
190
-		return err
177
+		return nil, err
191 178
 	}
192 179
 
193 180
 	// ensure that the commands are valid
194 181
 	for _, n := range ast.Children {
195 182
 		if !validCommitCommands[n.Value] {
196
-			return fmt.Errorf("%s is not a valid change command", n.Value)
183
+			return nil, fmt.Errorf("%s is not a valid change command", n.Value)
197 184
 		}
198 185
 	}
199 186
 
200 187
 	builder := &Builder{
201
-		Daemon:        b.Daemon,
202
-		Engine:        b.Engine,
203
-		Config:        &newConfig,
188
+		Daemon:        d,
189
+		Engine:        e,
190
+		Config:        c,
204 191
 		OutStream:     ioutil.Discard,
205 192
 		ErrStream:     ioutil.Discard,
206 193
 		disableCommit: true,
... ...
@@ -208,12 +212,32 @@ func (b *BuilderJob) CmdBuildConfig(job *engine.Job) error {
208 208
 
209 209
 	for i, n := range ast.Children {
210 210
 		if err := builder.dispatch(i, n); err != nil {
211
-			return err
211
+			return nil, err
212 212
 		}
213 213
 	}
214 214
 
215
-	if err := json.NewEncoder(job.Stdout).Encode(builder.Config); err != nil {
216
-		return err
215
+	return builder.Config, nil
216
+}
217
+
218
+func Commit(d *daemon.Daemon, eng *engine.Engine, name string, c *daemon.ContainerCommitConfig) (string, error) {
219
+	container, err := d.Get(name)
220
+	if err != nil {
221
+		return "", err
217 222
 	}
218
-	return nil
223
+
224
+	newConfig, err := BuildFromConfig(d, eng, c.Config, c.Changes)
225
+	if err != nil {
226
+		return "", err
227
+	}
228
+
229
+	if err := runconfig.Merge(newConfig, container.Config); err != nil {
230
+		return "", err
231
+	}
232
+
233
+	img, err := d.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, newConfig)
234
+	if err != nil {
235
+		return "", err
236
+	}
237
+
238
+	return img.ID, nil
219 239
 }
... ...
@@ -1,12 +1,6 @@
1 1
 package daemon
2 2
 
3 3
 import (
4
-	"bytes"
5
-	"encoding/json"
6
-	"io"
7
-
8
-	"github.com/Sirupsen/logrus"
9
-	"github.com/docker/docker/engine"
10 4
 	"github.com/docker/docker/image"
11 5
 	"github.com/docker/docker/runconfig"
12 6
 )
... ...
@@ -18,49 +12,7 @@ type ContainerCommitConfig struct {
18 18
 	Author  string
19 19
 	Comment string
20 20
 	Changes []string
21
-	Config  io.ReadCloser
22
-}
23
-
24
-func (daemon *Daemon) ContainerCommit(name string, c *ContainerCommitConfig) (string, error) {
25
-	container, err := daemon.Get(name)
26
-	if err != nil {
27
-		return "", err
28
-	}
29
-
30
-	var (
31
-		subenv       engine.Env
32
-		config       = container.Config
33
-		stdoutBuffer = bytes.NewBuffer(nil)
34
-		newConfig    runconfig.Config
35
-	)
36
-
37
-	if err := subenv.Decode(c.Config); err != nil {
38
-		logrus.Errorf("%s", err)
39
-	}
40
-
41
-	buildConfigJob := daemon.eng.Job("build_config")
42
-	buildConfigJob.Stdout.Add(stdoutBuffer)
43
-	buildConfigJob.SetenvList("changes", c.Changes)
44
-	// FIXME this should be remove when we remove deprecated config param
45
-	buildConfigJob.SetenvSubEnv("config", &subenv)
46
-
47
-	if err := buildConfigJob.Run(); err != nil {
48
-		return "", err
49
-	}
50
-	if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil {
51
-		return "", err
52
-	}
53
-
54
-	if err := runconfig.Merge(&newConfig, config); err != nil {
55
-		return "", err
56
-	}
57
-
58
-	img, err := daemon.Commit(container, c.Repo, c.Tag, c.Comment, c.Author, c.Pause, &newConfig)
59
-	if err != nil {
60
-		return "", err
61
-	}
62
-
63
-	return img.ID, nil
21
+	Config  *runconfig.Config
64 22
 }
65 23
 
66 24
 // Commit creates a new filesystem image from the current state of a container.
... ...
@@ -11,7 +11,6 @@ import (
11 11
 	"github.com/Sirupsen/logrus"
12 12
 	apiserver "github.com/docker/docker/api/server"
13 13
 	"github.com/docker/docker/autogen/dockerversion"
14
-	"github.com/docker/docker/builder"
15 14
 	"github.com/docker/docker/daemon"
16 15
 	_ "github.com/docker/docker/daemon/execdriver/lxc"
17 16
 	_ "github.com/docker/docker/daemon/execdriver/native"
... ...
@@ -141,9 +140,6 @@ func mainDaemon() {
141 141
 		"graphdriver": d.GraphDriver().String(),
142 142
 	}).Info("Docker daemon")
143 143
 
144
-	b := &builder.BuilderJob{eng, d}
145
-	b.Install()
146
-
147 144
 	// after the daemon is done setting up we can tell the api to start
148 145
 	// accepting connections with specified daemon
149 146
 	api.AcceptConnections(d)
... ...
@@ -155,7 +151,6 @@ func mainDaemon() {
155 155
 	if errAPI != nil {
156 156
 		logrus.Fatalf("Shutting down due to ServeAPI error: %v", errAPI)
157 157
 	}
158
-
159 158
 }
160 159
 
161 160
 // currentUserIsOwner checks whether the current user is the owner of the given
... ...
@@ -1,13 +1,10 @@
1 1
 package graph
2 2
 
3 3
 import (
4
-	"bytes"
5
-	"encoding/json"
6 4
 	"io"
7 5
 	"net/http"
8 6
 	"net/url"
9 7
 
10
-	"github.com/docker/docker/engine"
11 8
 	"github.com/docker/docker/pkg/archive"
12 9
 	"github.com/docker/docker/pkg/httputils"
13 10
 	"github.com/docker/docker/pkg/progressreader"
... ...
@@ -17,20 +14,18 @@ import (
17 17
 )
18 18
 
19 19
 type ImageImportConfig struct {
20
-	Changes   []string
21
-	InConfig  io.ReadCloser
22
-	Json      bool
23
-	OutStream io.Writer
24
-	//OutStream WriteFlusher
20
+	Changes         []string
21
+	InConfig        io.ReadCloser
22
+	Json            bool
23
+	OutStream       io.Writer
24
+	ContainerConfig *runconfig.Config
25 25
 }
26 26
 
27
-func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig, eng *engine.Engine) error {
27
+func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig *ImageImportConfig) error {
28 28
 	var (
29
-		sf           = streamformatter.NewStreamFormatter(imageImportConfig.Json)
30
-		archive      archive.ArchiveReader
31
-		resp         *http.Response
32
-		stdoutBuffer = bytes.NewBuffer(nil)
33
-		newConfig    runconfig.Config
29
+		sf      = streamformatter.NewStreamFormatter(imageImportConfig.Json)
30
+		archive archive.ArchiveReader
31
+		resp    *http.Response
34 32
 	)
35 33
 
36 34
 	if src == "-" {
... ...
@@ -63,20 +58,7 @@ func (s *TagStore) Import(src string, repo string, tag string, imageImportConfig
63 63
 		archive = progressReader
64 64
 	}
65 65
 
66
-	buildConfigJob := eng.Job("build_config")
67
-	buildConfigJob.Stdout.Add(stdoutBuffer)
68
-	buildConfigJob.SetenvList("changes", imageImportConfig.Changes)
69
-	// FIXME this should be remove when we remove deprecated config param
70
-	//buildConfigJob.Setenv("config", job.Getenv("config"))
71
-
72
-	if err := buildConfigJob.Run(); err != nil {
73
-		return err
74
-	}
75
-	if err := json.NewDecoder(stdoutBuffer).Decode(&newConfig); err != nil {
76
-		return err
77
-	}
78
-
79
-	img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, &newConfig)
66
+	img, err := s.graph.Create(archive, "", "", "Imported from "+src, "", nil, imageImportConfig.ContainerConfig)
80 67
 	if err != nil {
81 68
 		return err
82 69
 	}
... ...
@@ -128,12 +128,14 @@ type WriteFlusher struct {
128 128
 	sync.Mutex
129 129
 	w       io.Writer
130 130
 	flusher http.Flusher
131
+	flushed bool
131 132
 }
132 133
 
133 134
 func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
134 135
 	wf.Lock()
135 136
 	defer wf.Unlock()
136 137
 	n, err = wf.w.Write(b)
138
+	wf.flushed = true
137 139
 	wf.flusher.Flush()
138 140
 	return n, err
139 141
 }
... ...
@@ -142,9 +144,16 @@ func (wf *WriteFlusher) Write(b []byte) (n int, err error) {
142 142
 func (wf *WriteFlusher) Flush() {
143 143
 	wf.Lock()
144 144
 	defer wf.Unlock()
145
+	wf.flushed = true
145 146
 	wf.flusher.Flush()
146 147
 }
147 148
 
149
+func (wf *WriteFlusher) Flushed() bool {
150
+	wf.Lock()
151
+	defer wf.Unlock()
152
+	return wf.flushed
153
+}
154
+
148 155
 func NewWriteFlusher(w io.Writer) *WriteFlusher {
149 156
 	var flusher http.Flusher
150 157
 	if f, ok := w.(http.Flusher); ok {