From 1225eddc79374c1ec1d54475a8874b8c215269a3 Mon Sep 17 00:00:00 2001 From: Victor Vieux Date: Sat, 16 Jan 2016 13:44:48 -0800 Subject: [PATCH] allow engine/name support in volumes Signed-off-by: Victor Vieux --- api/handlers.go | 18 ++++--- cluster/cluster.go | 5 +- cluster/engine.go | 4 +- cluster/mesos/cluster.go | 7 +-- cluster/network.go | 2 +- cluster/swarm/cluster.go | 90 ++++++++++++++++++-------------- cluster/volume.go | 39 ++++++++++++++ test/integration/api/volume.bats | 10 +++- 8 files changed, 116 insertions(+), 59 deletions(-) diff --git a/api/handlers.go b/api/handlers.go index 77ce4aa97c..86e3438fbb 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -223,9 +223,15 @@ func getNetworks(c *context, w http.ResponseWriter, r *http.Request) { // GET /volumes func getVolumes(c *context, w http.ResponseWriter, r *http.Request) { - volumes := struct { - Volumes []*cluster.Volume - }{c.cluster.Volumes()} + volumes := struct{ Volumes []*dockerclient.Volume }{} + + for _, volume := range c.cluster.Volumes() { + tmp := (*volume).Volume + if tmp.Driver == "local" { + tmp.Name = volume.Engine.Name + "/" + volume.Name + } + volumes.Volumes = append(volumes.Volumes, &tmp) + } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(volumes) @@ -808,9 +814,9 @@ func proxyNetwork(c *context, w http.ResponseWriter, r *http.Request) { // Proxy a request to the right node func proxyVolume(c *context, w http.ResponseWriter, r *http.Request) { var name = mux.Vars(r)["volumename"] - if volume := c.cluster.Volume(name); volume != nil { - err := proxy(c.tlsConfig, volume.Engine.Addr, w, r) - volume.Engine.CheckConnectionErr(err) + if volume := c.cluster.Volumes().Get(name); volume != nil { + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(volume) return } httpError(w, fmt.Sprintf("No such volume: %s", name), http.StatusNotFound) diff --git a/cluster/cluster.go b/cluster/cluster.go index 6169683f2b..480b2c6eee 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -44,10 +44,7 @@ type Cluster interface { CreateVolume(request *dockerclient.VolumeCreateRequest) (*Volume, error) // Return all volumes - Volumes() []*Volume - - // Return one volume from the cluster - Volume(name string) *Volume + Volumes() Volumes // Remove volumes from the cluster RemoveVolumes(name string) (bool, error) diff --git a/cluster/engine.go b/cluster/engine.go index f091eda0db..f173e41df5 100644 --- a/cluster/engine.go +++ b/cluster/engine.go @@ -857,10 +857,10 @@ func (e *Engine) Networks() Networks { } // Volumes returns all the volumes in the engine -func (e *Engine) Volumes() []*Volume { +func (e *Engine) Volumes() Volumes { e.RLock() - volumes := make([]*Volume, 0, len(e.volumes)) + volumes := Volumes{} for _, volume := range e.volumes { volumes = append(volumes, volume) } diff --git a/cluster/mesos/cluster.go b/cluster/mesos/cluster.go index e2bb9638f5..fd08f5cb1e 100644 --- a/cluster/mesos/cluster.go +++ b/cluster/mesos/cluster.go @@ -338,12 +338,7 @@ func (c *Cluster) Networks() cluster.Networks { } // Volumes returns all the volumes in the cluster. -func (c *Cluster) Volumes() []*cluster.Volume { - return nil -} - -// Volume returns the volume name in the cluster -func (c *Cluster) Volume(name string) *cluster.Volume { +func (c *Cluster) Volumes() cluster.Volumes { return nil } diff --git a/cluster/network.go b/cluster/network.go index 498c15e74a..ca02a2f1d6 100644 --- a/cluster/network.go +++ b/cluster/network.go @@ -14,7 +14,7 @@ type Network struct { Engine *Engine } -// Networks represents a map of networks +// Networks represents an array of networks type Networks []*Network // Uniq returns all uniq networks diff --git a/cluster/swarm/cluster.go b/cluster/swarm/cluster.go index 4cd5d96ebe..2d42aab498 100644 --- a/cluster/swarm/cluster.go +++ b/cluster/swarm/cluster.go @@ -441,6 +441,18 @@ func (c *Cluster) refreshNetworks() { wg.Wait() } +func (c *Cluster) refreshVolumes() { + var wg sync.WaitGroup + for _, e := range c.engines { + wg.Add(1) + go func(e *cluster.Engine) { + e.RefreshVolumes() + wg.Done() + }(e) + } + wg.Wait() +} + // CreateNetwork creates a network in the cluster func (c *Cluster) CreateNetwork(request *dockerclient.NetworkCreate) (response *dockerclient.NetworkCreateResponse, err error) { var ( @@ -472,20 +484,45 @@ func (c *Cluster) CreateVolume(request *dockerclient.VolumeCreateRequest) (*clus wg sync.WaitGroup volume *cluster.Volume err error + parts = strings.SplitN(request.Name, "/", 2) + node = "" ) if request.Name == "" { request.Name = stringid.GenerateRandomID() + } else if len(parts) == 2 { + node = parts[0] + request.Name = parts[1] } + if node == "" { + c.RLock() + for _, e := range c.engines { + wg.Add(1) - c.RLock() - for _, e := range c.engines { - wg.Add(1) + go func(engine *cluster.Engine) { + defer wg.Done() - go func(engine *cluster.Engine) { - defer wg.Done() + v, er := engine.CreateVolume(request) + if v != nil { + volume = v + err = nil + } + if er != nil && volume == nil { + err = er + } + }(e) + } + c.RUnlock() - v, er := engine.CreateVolume(request) + wg.Wait() + } else { + config := cluster.BuildContainerConfig(dockerclient.ContainerConfig{Env: []string{"constraint:node==" + parts[0]}}) + nodes, err := c.scheduler.SelectNodesForContainer(c.listNodes(), config) + if err != nil { + return nil, err + } + if nodes != nil { + v, er := c.engines[nodes[0].ID].CreateVolume(request) if v != nil { volume = v err = nil @@ -493,11 +530,8 @@ func (c *Cluster) CreateVolume(request *dockerclient.VolumeCreateRequest) (*clus if er != nil && volume == nil { err = er } - }(e) + } } - c.RUnlock() - - wg.Wait() return volume, err } @@ -511,14 +545,12 @@ func (c *Cluster) RemoveVolumes(name string) (bool, error) { errs := []string{} var err error for _, e := range c.engines { - for _, volume := range e.Volumes() { - if volume.Name == name { - if err := volume.Engine.RemoveVolume(name); err != nil { - errs = append(errs, fmt.Sprintf("%s: %s", volume.Engine.Name, err.Error())) - continue - } - found = true + if volume := e.Volumes().Get(name); volume != nil { + if err := volume.Engine.RemoveVolume(volume.Name); err != nil { + errs = append(errs, fmt.Sprintf("%s: %s", volume.Engine.Name, err.Error())) + continue } + found = true } } if len(errs) > 0 { @@ -723,11 +755,11 @@ func (c *Cluster) Networks() cluster.Networks { } // Volumes returns all the volumes in the cluster. -func (c *Cluster) Volumes() []*cluster.Volume { +func (c *Cluster) Volumes() cluster.Volumes { c.RLock() defer c.RUnlock() - out := []*cluster.Volume{} + out := cluster.Volumes{} for _, e := range c.engines { out = append(out, e.Volumes()...) } @@ -735,26 +767,6 @@ func (c *Cluster) Volumes() []*cluster.Volume { return out } -// Volume returns the volume name in the cluster -func (c *Cluster) Volume(name string) *cluster.Volume { - // Abort immediately if the name is empty. - if len(name) == 0 { - return nil - } - - c.RLock() - defer c.RUnlock() - - for _, e := range c.engines { - for _, v := range e.Volumes() { - if v.Name == name { - return v - } - } - } - return nil -} - // listNodes returns all validated engines in the cluster, excluding pendingEngines. func (c *Cluster) listNodes() []*node.Node { c.RLock() diff --git a/cluster/volume.go b/cluster/volume.go index ba31547431..394d097bf6 100644 --- a/cluster/volume.go +++ b/cluster/volume.go @@ -8,3 +8,42 @@ type Volume struct { Engine *Engine } + +// Volumes represents an array of volumes +type Volumes []*Volume + +// Get returns a volume using it's ID or Name +func (volumes Volumes) Get(name string) *Volume { + // Abort immediately if the name is empty. + if len(name) == 0 { + return nil + } + + candidates := []*Volume{} + + // Match name, /name or engine/name. + for _, volume := range volumes { + if volume.Name == name || volume.Engine.ID+"/"+volume.Name == name || volume.Engine.Name+"/"+volume.Name == name { + candidates = append(candidates, volume) + } + } + + if size := len(candidates); size == 1 { + return candidates[0] + } else if size > 1 { + return nil + } + + // Match name, /name or engine/name. + for _, volume := range volumes { + if volume.Name == "/"+name { + return volume + } + } + + if len(candidates) == 1 { + return candidates[0] + } + + return nil +} diff --git a/test/integration/api/volume.bats b/test/integration/api/volume.bats index 917c7017f6..66e6e0aada 100644 --- a/test/integration/api/volume.bats +++ b/test/integration/api/volume.bats @@ -32,10 +32,11 @@ function teardown() { swarm_manage # run - docker_swarm run -d -v=/tmp busybox true + docker_swarm run -d -v=/tmp -e constraint:node==node-0 busybox true run docker_swarm volume ls -q [ "${#lines[@]}" -eq 1 ] + [[ "${output}" == *"node-0/"* ]] run docker_swarm volume inspect ${output} [ "${#lines[@]}" -eq 7 ] @@ -56,6 +57,13 @@ function teardown() { docker_swarm run -d -v=/tmp busybox true run docker_swarm volume ls [ "${#lines[@]}" -eq 4 ] + + run docker_swarm volume create --name=node-2/test_volume2 + [ "$status" -ne 0 ] + + docker_swarm volume create --name=node-0/test_volume2 + run docker_swarm volume ls + [ "${#lines[@]}" -eq 5 ] } @test "docker volume rm" {