From 8506acbed2c07a040d02f3e595befdfadd360114 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 8 Apr 2015 17:14:20 -0700 Subject: [PATCH 1/3] fix proxyRandom even without container Signed-off-by: Victor Vieux --- api/handlers.go | 22 ++++++++-------------- cluster/cluster.go | 4 ++++ cluster/swarm/cluster.go | 17 +++++++++++++++++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/api/handlers.go b/api/handlers.go index 6621425370..65c82a0652 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -5,7 +5,6 @@ import ( "encoding/json" "fmt" "io/ioutil" - "math/rand" "net/http" "runtime" "sort" @@ -14,8 +13,6 @@ import ( dockerfilters "github.com/docker/docker/pkg/parsers/filters" "github.com/docker/swarm/cluster" - "github.com/docker/swarm/scheduler/filter" - "github.com/docker/swarm/scheduler/node" "github.com/docker/swarm/version" "github.com/gorilla/mux" "github.com/samalba/dockerclient" @@ -398,24 +395,21 @@ func proxyImage(c *context, w http.ResponseWriter, r *http.Request) { // Proxy a request to a random node func proxyRandom(c *context, w http.ResponseWriter, r *http.Request) { - candidates := []*node.Node{} - - // FIXME: doesn't work if there are no container in the cluster - // remove proxyRandom and implemente the features locally - for _, container := range c.cluster.Containers() { - candidates = append(candidates, node.NewNode(container.Engine)) - } - - healthFilter := &filter.HealthFilter{} - accepted, err := healthFilter.Filter(nil, candidates) + engine, err := c.cluster.RandomEngine_() if err != nil { httpError(w, err.Error(), http.StatusInternalServerError) return } - if err := proxy(c.tlsConfig, accepted[rand.Intn(len(accepted))].Addr, w, r); err != nil { + if engine == nil { + httpError(w, "no node available in the cluster", http.StatusInternalServerError) + return + } + + if err := proxy(c.tlsConfig, engine.Addr, w, r); err != nil { httpError(w, err.Error(), http.StatusInternalServerError) } + } // POST /commit diff --git a/cluster/cluster.go b/cluster/cluster.go index b258164266..5723aa9334 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -39,4 +39,8 @@ type Cluster interface { // Register an event handler for cluster-wide events. RegisterEventHandler(h EventHandler) error + + // FIXME: remove this method + // Return a random engine + RandomEngine_() (*Engine, error) } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 7c7f4549ed..12d833ed06 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -3,6 +3,7 @@ package swarm import ( "errors" "fmt" + "math/rand" "sort" "sync" @@ -11,6 +12,7 @@ import ( "github.com/docker/swarm/cluster" "github.com/docker/swarm/discovery" "github.com/docker/swarm/scheduler" + "github.com/docker/swarm/scheduler/filter" "github.com/docker/swarm/scheduler/node" "github.com/docker/swarm/state" "github.com/samalba/dockerclient" @@ -307,3 +309,18 @@ func (c *Cluster) Info() [][2]string { return info } + +// RandomEngine_ returns a random engine. +func (c *Cluster) RandomEngine_() (*cluster.Engine, error) { + healthFilter := &filter.HealthFilter{} + accepted, err := healthFilter.Filter(nil, c.listNodes()) + if err != nil { + return nil, err + } + if size := len(accepted); size > 0 { + if n, ok := c.engines[accepted[rand.Intn(size)].ID]; ok { + return n, nil + } + } + return nil, nil +} From d74e87295262131663355f39ba87d154185977bb Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Wed, 8 Apr 2015 17:14:52 -0700 Subject: [PATCH 2/3] add build improve random selection Signed-off-by: Victor Vieux --- api/README.md | 1 - api/handlers.go | 2 +- api/router.go | 2 +- cluster/cluster.go | 2 +- cluster/swarm/cluster.go | 15 +++++---------- 5 files changed, 8 insertions(+), 14 deletions(-) diff --git a/api/README.md b/api/README.md index 014538cecb..6c83bb242d 100644 --- a/api/README.md +++ b/api/README.md @@ -15,7 +15,6 @@ Some endpoints have not yet been implemented and will return a 404 error. ``` GET "/images/get" -POST "/build" POST "/images/load" POST "/images/create" : "docker import" flow not implement ``` diff --git a/api/handlers.go b/api/handlers.go index 65c82a0652..ff64d13bdc 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -395,7 +395,7 @@ func proxyImage(c *context, w http.ResponseWriter, r *http.Request) { // Proxy a request to a random node func proxyRandom(c *context, w http.ResponseWriter, r *http.Request) { - engine, err := c.cluster.RandomEngine_() + engine, err := c.cluster.RANDOMENGINE() if err != nil { httpError(w, err.Error(), http.StatusInternalServerError) return diff --git a/api/router.go b/api/router.go index 6fc7955530..1e192788b3 100644 --- a/api/router.go +++ b/api/router.go @@ -46,7 +46,7 @@ var routes = map[string]map[string]handler{ "POST": { "/auth": proxyRandom, "/commit": postCommit, - "/build": notImplementedHandler, + "/build": proxyRandom, "/images/create": postImagesCreate, "/images/load": notImplementedHandler, "/images/{name:.*}/push": proxyImage, diff --git a/cluster/cluster.go b/cluster/cluster.go index 5723aa9334..3b0beaab7d 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -42,5 +42,5 @@ type Cluster interface { // FIXME: remove this method // Return a random engine - RandomEngine_() (*Engine, error) + RANDOMENGINE() (*Engine, error) } diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 12d833ed06..483f6092c2 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -3,7 +3,6 @@ package swarm import ( "errors" "fmt" - "math/rand" "sort" "sync" @@ -12,7 +11,6 @@ import ( "github.com/docker/swarm/cluster" "github.com/docker/swarm/discovery" "github.com/docker/swarm/scheduler" - "github.com/docker/swarm/scheduler/filter" "github.com/docker/swarm/scheduler/node" "github.com/docker/swarm/state" "github.com/samalba/dockerclient" @@ -310,17 +308,14 @@ func (c *Cluster) Info() [][2]string { return info } -// RandomEngine_ returns a random engine. -func (c *Cluster) RandomEngine_() (*cluster.Engine, error) { - healthFilter := &filter.HealthFilter{} - accepted, err := healthFilter.Filter(nil, c.listNodes()) +// RANDOMENGINE returns a random engine. +func (c *Cluster) RANDOMENGINE() (*cluster.Engine, error) { + n, err := c.scheduler.SelectNodeForContainer(c.listNodes(), &dockerclient.ContainerConfig{}) if err != nil { return nil, err } - if size := len(accepted); size > 0 { - if n, ok := c.engines[accepted[rand.Intn(size)].ID]; ok { - return n, nil - } + if n != nil { + return c.engines[n.ID], nil } return nil, nil } From 05b01c7b434621f0f9cde2ebb35f8d5c050470f5 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Tue, 14 Apr 2015 02:23:42 +0000 Subject: [PATCH 3/3] forcerefresh after build and add test Signed-off-by: Victor Vieux --- api/handlers.go | 25 ++++++++++++++++++++++ api/router.go | 2 +- api/utils.go | 10 ++++++++- cluster/engine.go | 10 ++++----- test/integration/api.bats | 15 +++++++++++-- test/integration/testdata/build/Dockerfile | 2 ++ 6 files changed, 55 insertions(+), 9 deletions(-) create mode 100644 test/integration/testdata/build/Dockerfile diff --git a/api/handlers.go b/api/handlers.go index ff64d13bdc..0671064982 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -412,6 +412,31 @@ func proxyRandom(c *context, w http.ResponseWriter, r *http.Request) { } +// Proxy a request to a random node and force refresh +func proxyRandomAndForceRefresh(c *context, w http.ResponseWriter, r *http.Request) { + engine, err := c.cluster.RANDOMENGINE() + if err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + return + } + + if engine == nil { + httpError(w, "no node available in the cluster", http.StatusInternalServerError) + return + } + + cb := func(resp *http.Response) { + if resp.StatusCode == http.StatusOK { + engine.RefreshImages() + } + } + + if err := proxyAsync(c.tlsConfig, engine.Addr, w, r, cb); err != nil { + httpError(w, err.Error(), http.StatusInternalServerError) + } + +} + // POST /commit func postCommit(c *context, w http.ResponseWriter, r *http.Request) { if err := r.ParseForm(); err != nil { diff --git a/api/router.go b/api/router.go index 1e192788b3..a402aed895 100644 --- a/api/router.go +++ b/api/router.go @@ -46,7 +46,7 @@ var routes = map[string]map[string]handler{ "POST": { "/auth": proxyRandom, "/commit": postCommit, - "/build": proxyRandom, + "/build": proxyRandomAndForceRefresh, "/images/create": postImagesCreate, "/images/load": notImplementedHandler, "/images/{name:.*}/push": proxyImage, diff --git a/api/utils.go b/api/utils.go index 7a4710d570..d66a6a8d50 100644 --- a/api/utils.go +++ b/api/utils.go @@ -63,7 +63,7 @@ func closeIdleConnections(client *http.Client) { } } -func proxy(tlsConfig *tls.Config, addr string, w http.ResponseWriter, r *http.Request) error { +func proxyAsync(tlsConfig *tls.Config, addr string, w http.ResponseWriter, r *http.Request, callback func(*http.Response)) error { // Use a new client for each request client, scheme := newClientAndScheme(tlsConfig) // RequestURI may not be sent to client @@ -86,9 +86,17 @@ func proxy(tlsConfig *tls.Config, addr string, w http.ResponseWriter, r *http.Re resp.Body.Close() closeIdleConnections(client) + if callback != nil { + callback(resp) + } + return nil } +func proxy(tlsConfig *tls.Config, addr string, w http.ResponseWriter, r *http.Request) error { + return proxyAsync(tlsConfig, addr, w, r, nil) +} + func hijack(tlsConfig *tls.Config, addr string, w http.ResponseWriter, r *http.Request) error { if parts := strings.SplitN(addr, "://", 2); len(parts) == 2 { addr = parts[1] diff --git a/cluster/engine.go b/cluster/engine.go index 856e0ce758..81d26bc0e4 100644 --- a/cluster/engine.go +++ b/cluster/engine.go @@ -92,7 +92,7 @@ func (e *Engine) connectClient(client dockerclient.Client) error { return err } - if err := e.refreshImages(); err != nil { + if err := e.RefreshImages(); err != nil { e.client = nil return err } @@ -155,8 +155,8 @@ func (e *Engine) RemoveImage(image *Image) ([]*dockerclient.ImageDelete, error) return e.client.RemoveImage(image.Id) } -// Refresh the list of images on the engine. -func (e *Engine) refreshImages() error { +// RefreshImages refreshes the list of images on the engine. +func (e *Engine) RefreshImages() error { images, err := e.client.ListImages() if err != nil { return err @@ -275,7 +275,7 @@ func (e *Engine) refreshLoop() { } if err == nil { - err = e.refreshImages() + err = e.RefreshImages() } if err != nil { @@ -490,7 +490,7 @@ func (e *Engine) handler(ev *dockerclient.Event, _ chan error, args ...interface case "pull", "untag", "delete": // These events refer to images so there's no need to update // containers. - e.refreshImages() + e.RefreshImages() case "start", "die": // If the container is started or stopped, we have to do an inspect in // order to get the new NetworkSettings. diff --git a/test/integration/api.bats b/test/integration/api.bats index 7060bbcd92..732d0f24c6 100644 --- a/test/integration/api.bats +++ b/test/integration/api.bats @@ -12,9 +12,20 @@ function teardown() { skip } -# FIXME @test "docker build" { - skip + start_docker 3 + swarm_manage + + run docker_swarm images + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 1 ] + + run docker_swarm build -t test $BATS_TEST_DIRNAME/testdata/build + [ "$status" -eq 0 ] + + run docker_swarm images + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] } # FIXME diff --git a/test/integration/testdata/build/Dockerfile b/test/integration/testdata/build/Dockerfile new file mode 100644 index 0000000000..bfdf522982 --- /dev/null +++ b/test/integration/testdata/build/Dockerfile @@ -0,0 +1,2 @@ +FROM busybox +RUN ls