From 7ad8a3705f191dc27296996c36cc1a8b055f6c1a Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 20 Jan 2015 18:39:52 +0000 Subject: [PATCH 1/3] add support for pull and rmi Signed-off-by: Victor Vieux --- api/README.md | 2 +- api/api.go | 30 +++++++++++++++++++++++++++++- cluster/cluster.go | 2 ++ cluster/swarm/cluster.go | 8 ++++++++ 4 files changed, 40 insertions(+), 2 deletions(-) diff --git a/api/README.md b/api/README.md index 7c25d88b93..d1f7491962 100644 --- a/api/README.md +++ b/api/README.md @@ -18,7 +18,7 @@ GET "/containers/{name:.*}/attach/ws" POST "/commit" POST "/build" -POST "/images/create" +POST "/images/create" (pull implemented) POST "/images/load" POST "/images/{name:.*}/push" POST "/images/{name:.*}/tag" diff --git a/api/api.go b/api/api.go index f183213b79..a9e375e82b 100644 --- a/api/api.go +++ b/api/api.go @@ -247,6 +247,34 @@ func deleteContainers(c *context, w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) } +// POST /images/create +func postImagesCreate(c *context, w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + + wf := NewWriteFlusher(w) + + if image := r.Form.Get("fromImage"); image != "" { //pull + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusCreated) + + if tag := r.Form.Get("tag"); tag != "" { + image += ":" + tag + } + begin := func(name string) { + fmt.Fprintf(wf, "{%q:%q,%q:\"Pulling %s...\",%q:{}}", "id", name, "status", image, "progressDetail") + } + end := func(name string) { + fmt.Fprintf(wf, "{%q:%q,%q:\"Pulling %s... : downloaded\",%q:{}}", "id", name, "status", image, "progressDetail") + } + c.cluster.Pull(image, begin, end) + } else { //import + httpError(w, "Not supported in clustering mode.", http.StatusNotImplemented) + } +} + // GET /events func getEvents(c *context, w http.ResponseWriter, r *http.Request) { c.eventsHandler.Add(r.RemoteAddr, w) @@ -416,7 +444,7 @@ func createRouter(c *context, enableCors bool) *mux.Router { "/auth": proxyRandom, "/commit": notImplementedHandler, "/build": notImplementedHandler, - "/images/create": notImplementedHandler, + "/images/create": postImagesCreate, "/images/load": notImplementedHandler, "/images/{name:.*}/push": notImplementedHandler, "/images/{name:.*}/tag": notImplementedHandler, diff --git a/cluster/cluster.go b/cluster/cluster.go index 986c878bd4..899cec97ac 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -11,5 +11,7 @@ type Cluster interface { Containers() []*Container Container(IdOrName string) *Container + Pull(name string, begin, end func(string)) + Info() [][2]string } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 1603547320..9fb30a4147 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -186,6 +186,14 @@ func (c *Cluster) Image(IdOrName string) *cluster.Image { return nil } +func (c *Cluster) Pull(name string, begin, end func(string)) { + for _, node := range c.nodes { + begin(node.Name()) + node.Pull(name) + end(node.Name()) + } +} + // Containers returns all the containers in the cluster. func (c *Cluster) Containers() []*cluster.Container { c.RLock() From e0fa132ec95abe91fe352003cb8e640c2e302e49 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 5 Mar 2015 12:42:09 -0800 Subject: [PATCH 2/3] add parallel Signed-off-by: Victor Vieux --- cluster/swarm/cluster.go | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 9fb30a4147..57623daf8c 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -187,10 +187,18 @@ func (c *Cluster) Image(IdOrName string) *cluster.Image { } func (c *Cluster) Pull(name string, begin, end func(string)) { - for _, node := range c.nodes { - begin(node.Name()) - node.Pull(name) - end(node.Name()) + size := len(c.nodes) + done := make(chan bool, size) + for _, n := range c.nodes { + go func(nn *node) { + begin(nn.Name()) + nn.Pull(name) + end(nn.Name()) + done <- true + }(n) + } + for i := 0; i < size; i++ { + <-done } } From 0e7f970d4d73544f0dca5698c0e7f9a2d9702341 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Thu, 5 Mar 2015 17:04:43 -0800 Subject: [PATCH 3/3] use a single callback Signed-off-by: Victor Vieux --- api/api.go | 13 +++++++------ cluster/cluster.go | 18 +++++++++++++++++- cluster/swarm/cluster.go | 12 ++++++++---- 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/api/api.go b/api/api.go index a9e375e82b..97275bad0b 100644 --- a/api/api.go +++ b/api/api.go @@ -263,13 +263,14 @@ func postImagesCreate(c *context, w http.ResponseWriter, r *http.Request) { if tag := r.Form.Get("tag"); tag != "" { image += ":" + tag } - begin := func(name string) { - fmt.Fprintf(wf, "{%q:%q,%q:\"Pulling %s...\",%q:{}}", "id", name, "status", image, "progressDetail") + callback := func(what, status string) { + if status == "" { + fmt.Fprintf(wf, "{%q:%q,%q:\"Pulling %s...\",%q:{}}", "id", what, "status", image, "progressDetail") + } else { + fmt.Fprintf(wf, "{%q:%q,%q:\"Pulling %s... : %s\",%q:{}}", "id", what, "status", image, status, "progressDetail") + } } - end := func(name string) { - fmt.Fprintf(wf, "{%q:%q,%q:\"Pulling %s... : downloaded\",%q:{}}", "id", name, "status", image, "progressDetail") - } - c.cluster.Pull(image, begin, end) + c.cluster.Pull(image, callback) } else { //import httpError(w, "Not supported in clustering mode.", http.StatusNotImplemented) } diff --git a/cluster/cluster.go b/cluster/cluster.go index 899cec97ac..1844363b35 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -3,15 +3,31 @@ package cluster import "github.com/samalba/dockerclient" type Cluster interface { + // Create a container CreateContainer(config *dockerclient.ContainerConfig, name string) (*Container, error) + + // Remove a container RemoveContainer(container *Container, force bool) error + // Return all images Images() []*Image + + // Return one image matching `IdOrName` Image(IdOrName string) *Image + + // Return all containers Containers() []*Container + + // Return container the matching `IdOrName` Container(IdOrName string) *Container - Pull(name string, begin, end func(string)) + // Pull images + // `callback` can be called multiple time + // `what` is what is being pulled + // `status` is the current status, like "", "in progress" or "downloaded + Pull(name string, callback func(what, status string)) + // Return some info about the cluster, like nb or containers / images + // It is pretty open, so the implementation decides what to return. Info() [][2]string } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 57623daf8c..4e712009ca 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -186,14 +186,18 @@ func (c *Cluster) Image(IdOrName string) *cluster.Image { return nil } -func (c *Cluster) Pull(name string, begin, end func(string)) { +func (c *Cluster) Pull(name string, callback func(what, status string)) { size := len(c.nodes) done := make(chan bool, size) for _, n := range c.nodes { go func(nn *node) { - begin(nn.Name()) - nn.Pull(name) - end(nn.Name()) + if callback != nil { + callback(nn.Name(), "") + } + nn.pull(name) + if callback != nil { + callback(nn.Name(), "downloaded") + } done <- true }(n) }