diff --git a/api.go b/api.go index 4391d5cd24..148202c2e3 100644 --- a/api.go +++ b/api.go @@ -510,24 +510,22 @@ func postImagesInsert(srv *Server, version float64, w http.ResponseWriter, r *ht if err := parseForm(r); err != nil { return err } - - url := r.Form.Get("url") - path := r.Form.Get("path") if vars == nil { return fmt.Errorf("Missing parameter") } - name := vars["name"] if version > 1.0 { w.Header().Set("Content-Type", "application/json") } - sf := utils.NewStreamFormatter(version > 1.0) - err := srv.ImageInsert(name, url, path, w, sf) - if err != nil { - if sf.Used() { - w.Write(sf.FormatError(err)) - return nil + + job := srv.Eng.Job("insert", vars["name"], r.Form.Get("url"), r.Form.Get("path")) + job.SetenvBool("json", version > 1.0) + job.Stdout.Add(w) + if err := job.Run(); err != nil { + if !job.Stdout.Used() { + return err } - return err + sf := utils.NewStreamFormatter(version > 1.0) + w.Write(sf.FormatError(err)) } return nil diff --git a/engine/streams.go b/engine/streams.go index 824f0a4ab2..fbce6e632a 100644 --- a/engine/streams.go +++ b/engine/streams.go @@ -12,6 +12,7 @@ type Output struct { sync.Mutex dests []io.Writer tasks sync.WaitGroup + used bool } // NewOutput returns a new Output object with no destinations attached. @@ -20,6 +21,13 @@ func NewOutput() *Output { return &Output{} } +// Return true if something was written on this output +func (o *Output) Used() bool { + o.Mutex.Lock() + defer o.Mutex.Unlock() + return o.used +} + // Add attaches a new destination to the Output. Any data subsequently written // to the output will be written to the new destination in addition to all the others. // This method is thread-safe. @@ -82,6 +90,7 @@ func (o *Output) AddString(dst *string) error { func (o *Output) Write(p []byte) (n int, err error) { o.Mutex.Lock() defer o.Mutex.Unlock() + o.used = true var firstErr error for _, dst := range o.dests { _, err := dst.Write(p) diff --git a/integration/server_test.go b/integration/server_test.go index e755d14728..6c26f56174 100644 --- a/integration/server_test.go +++ b/integration/server_test.go @@ -2,8 +2,6 @@ package docker import ( "github.com/dotcloud/docker" - "github.com/dotcloud/docker/utils" - "io/ioutil" "strings" "testing" ) @@ -365,20 +363,19 @@ func TestImageInsert(t *testing.T) { eng := NewTestEngine(t) defer mkRuntimeFromEngine(eng, t).Nuke() srv := mkServerFromEngine(eng, t) - sf := utils.NewStreamFormatter(true) // bad image name fails - if err := srv.ImageInsert("foo", "https://www.docker.io/static/img/docker-top-logo.png", "/foo", ioutil.Discard, sf); err == nil { + if err := srv.Eng.Job("insert", "foo", "https://www.docker.io/static/img/docker-top-logo.png", "/foo").Run(); err == nil { t.Fatal("expected an error and got none") } // bad url fails - if err := srv.ImageInsert(unitTestImageID, "http://bad_host_name_that_will_totally_fail.com/", "/foo", ioutil.Discard, sf); err == nil { + if err := srv.Eng.Job("insert", unitTestImageID, "http://bad_host_name_that_will_totally_fail.com/", "/foo").Run(); err == nil { t.Fatal("expected an error and got none") } // success returns nil - if err := srv.ImageInsert(unitTestImageID, "https://www.docker.io/static/img/docker-top-logo.png", "/foo", ioutil.Discard, sf); err != nil { + if err := srv.Eng.Job("insert", unitTestImageID, "https://www.docker.io/static/img/docker-top-logo.png", "/foo").Run(); err != nil { t.Fatalf("expected no error, but got %v", err) } } diff --git a/server.go b/server.go index 01e986a00b..2057b477a1 100644 --- a/server.go +++ b/server.go @@ -143,6 +143,10 @@ func jobInitApi(job *engine.Job) engine.Status { job.Error(err) return engine.StatusErr } + if err := job.Eng.Register("insert", srv.ImageInsert); err != nil { + job.Error(err) + return engine.StatusErr + } return engine.StatusOK } @@ -511,39 +515,58 @@ func (srv *Server) ImagesSearch(term string) ([]registry.SearchResult, error) { return results.Results, nil } -func (srv *Server) ImageInsert(name, url, path string, out io.Writer, sf *utils.StreamFormatter) error { - out = utils.NewWriteFlusher(out) +func (srv *Server) ImageInsert(job *engine.Job) engine.Status { + if len(job.Args) != 3 { + job.Errorf("Usage: %s IMAGE URL PATH\n", job.Name) + return engine.StatusErr + } + + var ( + name = job.Args[0] + url = job.Args[1] + path = job.Args[2] + ) + + sf := utils.NewStreamFormatter(job.GetenvBool("json")) + + out := utils.NewWriteFlusher(job.Stdout) img, err := srv.runtime.repositories.LookupImage(name) if err != nil { - return err + job.Error(err) + return engine.StatusErr } file, err := utils.Download(url) if err != nil { - return err + job.Error(err) + return engine.StatusErr } defer file.Body.Close() config, _, _, err := ParseRun([]string{img.ID, "echo", "insert", url, path}, srv.runtime.capabilities) if err != nil { - return err + job.Error(err) + return engine.StatusErr } c, _, err := srv.runtime.Create(config, "") if err != nil { - return err + job.Error(err) + return engine.StatusErr } if err := c.Inject(utils.ProgressReader(file.Body, int(file.ContentLength), out, sf, false, "", "Downloading"), path); err != nil { - return err + job.Error(err) + return engine.StatusErr } // FIXME: Handle custom repo, tag comment, author img, err = srv.runtime.Commit(c, "", "", img.Comment, img.Author, nil) if err != nil { - return err + out.Write(sf.FormatError(err)) + return engine.StatusErr } - out.Write(sf.FormatStatus(img.ID, "")) - return nil + out.Write(sf.FormatStatus("", img.ID)) + return engine.StatusOK } func (srv *Server) ImagesViz(job *engine.Job) engine.Status {