Remove engine.Job from Builder.
| 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 = ®istry.AuthConfig{}
|
| 1248 | 1248 |
configFileEncoded = r.Header.Get("X-Registry-Config")
|
| 1249 | 1249 |
configFile = ®istry.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: ®istry.AuthConfig{},
|
|
| 81 |
+ ConfigFile: ®istry.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 = ®istry.AuthConfig{}
|
|
| 68 |
- configFile = ®istry.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 {
|