Merge pull request #1666 from vieux/volumes_name

allow engine/name support in volumes
This commit is contained in:
Alexandre Beslic 2016-01-22 16:46:24 -08:00
commit 50ad1548d9
9 changed files with 141 additions and 82 deletions

View File

@ -221,11 +221,39 @@ func getNetworks(c *context, w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(out) json.NewEncoder(w).Encode(out)
} }
// GET /networks/{networkid:.*}
func getNetwork(c *context, w http.ResponseWriter, r *http.Request) {
var id = mux.Vars(r)["networkid"]
if network := c.cluster.Networks().Uniq().Get(id); network != nil {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(network)
return
}
httpError(w, fmt.Sprintf("No such network: %s", id), http.StatusNotFound)
}
// GET /volumes/{volumename:.*}
func getVolume(c *context, w http.ResponseWriter, r *http.Request) {
var name = mux.Vars(r)["volumename"]
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)
}
// GET /volumes // GET /volumes
func getVolumes(c *context, w http.ResponseWriter, r *http.Request) { func getVolumes(c *context, w http.ResponseWriter, r *http.Request) {
volumes := struct { volumes := struct{ Volumes []*dockerclient.Volume }{}
Volumes []*cluster.Volume
}{c.cluster.Volumes()} 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") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(volumes) json.NewEncoder(w).Encode(volumes)
@ -794,28 +822,6 @@ func ping(c *context, w http.ResponseWriter, r *http.Request) {
w.Write([]byte{'O', 'K'}) w.Write([]byte{'O', 'K'})
} }
// Proxy a request to the right node
func proxyNetwork(c *context, w http.ResponseWriter, r *http.Request) {
var id = mux.Vars(r)["networkid"]
if network := c.cluster.Networks().Uniq().Get(id); network != nil {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(network)
return
}
httpError(w, fmt.Sprintf("No such network: %s", id), http.StatusNotFound)
}
// 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)
return
}
httpError(w, fmt.Sprintf("No such volume: %s", name), http.StatusNotFound)
}
// Proxy network to container operations, including connect/disconnect request // Proxy network to container operations, including connect/disconnect request
func proxyNetworkContainerOperation(c *context, w http.ResponseWriter, r *http.Request) { func proxyNetworkContainerOperation(c *context, w http.ResponseWriter, r *http.Request) {
var networkid = mux.Vars(r)["networkid"] var networkid = mux.Vars(r)["networkid"]

View File

@ -50,9 +50,9 @@ var routes = map[string]map[string]handler{
"/containers/{name:.*}/attach/ws": proxyHijack, "/containers/{name:.*}/attach/ws": proxyHijack,
"/exec/{execid:.*}/json": proxyContainer, "/exec/{execid:.*}/json": proxyContainer,
"/networks": getNetworks, "/networks": getNetworks,
"/networks/{networkid:.*}": proxyNetwork, "/networks/{networkid:.*}": getNetwork,
"/volumes": getVolumes, "/volumes": getVolumes,
"/volumes/{volumename:.*}": proxyVolume, "/volumes/{volumename:.*}": getVolume,
}, },
"POST": { "POST": {
"/auth": proxyRandom, "/auth": proxyRandom,

View File

@ -44,10 +44,7 @@ type Cluster interface {
CreateVolume(request *dockerclient.VolumeCreateRequest) (*Volume, error) CreateVolume(request *dockerclient.VolumeCreateRequest) (*Volume, error)
// Return all volumes // Return all volumes
Volumes() []*Volume Volumes() Volumes
// Return one volume from the cluster
Volume(name string) *Volume
// Remove volumes from the cluster // Remove volumes from the cluster
RemoveVolumes(name string) (bool, error) RemoveVolumes(name string) (bool, error)

View File

@ -857,10 +857,10 @@ func (e *Engine) Networks() Networks {
} }
// Volumes returns all the volumes in the engine // Volumes returns all the volumes in the engine
func (e *Engine) Volumes() []*Volume { func (e *Engine) Volumes() Volumes {
e.RLock() e.RLock()
volumes := make([]*Volume, 0, len(e.volumes)) volumes := Volumes{}
for _, volume := range e.volumes { for _, volume := range e.volumes {
volumes = append(volumes, volume) volumes = append(volumes, volume)
} }

View File

@ -338,12 +338,7 @@ func (c *Cluster) Networks() cluster.Networks {
} }
// Volumes returns all the volumes in the cluster. // Volumes returns all the volumes in the cluster.
func (c *Cluster) Volumes() []*cluster.Volume { func (c *Cluster) Volumes() cluster.Volumes {
return nil
}
// Volume returns the volume name in the cluster
func (c *Cluster) Volume(name string) *cluster.Volume {
return nil return nil
} }

View File

@ -14,7 +14,7 @@ type Network struct {
Engine *Engine Engine *Engine
} }
// Networks represents a map of networks // Networks represents an array of networks
type Networks []*Network type Networks []*Network
// Uniq returns all uniq networks // Uniq returns all uniq networks
@ -67,20 +67,21 @@ func (networks Networks) Get(IDOrName string) *Network {
candidates := []*Network{} candidates := []*Network{}
// Match name, /name or engine/name. // Match name or engine/name.
for _, network := range networks { for _, network := range networks {
if network.Name == IDOrName || network.Engine.ID+"/"+network.Name == IDOrName || network.Engine.Name+"/"+network.Name == IDOrName { if network.Name == IDOrName || network.Engine.ID+"/"+network.Name == IDOrName || network.Engine.Name+"/"+network.Name == IDOrName {
candidates = append(candidates, network) candidates = append(candidates, network)
} }
} }
// Return if we found a unique match.
if size := len(candidates); size == 1 { if size := len(candidates); size == 1 {
return candidates[0] return candidates[0]
} else if size > 1 { } else if size > 1 {
return nil return nil
} }
// Match name, /name or engine/name. // Match /name and return as soon as we find one.
for _, network := range networks { for _, network := range networks {
if network.Name == "/"+IDOrName { if network.Name == "/"+IDOrName {
return network return network

View File

@ -441,6 +441,18 @@ func (c *Cluster) refreshNetworks() {
wg.Wait() 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 // CreateNetwork creates a network in the cluster
func (c *Cluster) CreateNetwork(request *dockerclient.NetworkCreate) (response *dockerclient.NetworkCreateResponse, err error) { func (c *Cluster) CreateNetwork(request *dockerclient.NetworkCreate) (response *dockerclient.NetworkCreateResponse, err error) {
var ( var (
@ -472,20 +484,45 @@ func (c *Cluster) CreateVolume(request *dockerclient.VolumeCreateRequest) (*clus
wg sync.WaitGroup wg sync.WaitGroup
volume *cluster.Volume volume *cluster.Volume
err error err error
parts = strings.SplitN(request.Name, "/", 2)
node = ""
) )
if request.Name == "" { if request.Name == "" {
request.Name = stringid.GenerateRandomID() 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() go func(engine *cluster.Engine) {
for _, e := range c.engines { defer wg.Done()
wg.Add(1)
go func(engine *cluster.Engine) { v, er := engine.CreateVolume(request)
defer wg.Done() 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 { if v != nil {
volume = v volume = v
err = nil err = nil
@ -493,11 +530,8 @@ func (c *Cluster) CreateVolume(request *dockerclient.VolumeCreateRequest) (*clus
if er != nil && volume == nil { if er != nil && volume == nil {
err = er err = er
} }
}(e) }
} }
c.RUnlock()
wg.Wait()
return volume, err return volume, err
} }
@ -511,14 +545,12 @@ func (c *Cluster) RemoveVolumes(name string) (bool, error) {
errs := []string{} errs := []string{}
var err error var err error
for _, e := range c.engines { for _, e := range c.engines {
for _, volume := range e.Volumes() { if volume := e.Volumes().Get(name); volume != nil {
if volume.Name == name { if err := volume.Engine.RemoveVolume(volume.Name); err != nil {
if err := volume.Engine.RemoveVolume(name); err != nil { errs = append(errs, fmt.Sprintf("%s: %s", volume.Engine.Name, err.Error()))
errs = append(errs, fmt.Sprintf("%s: %s", volume.Engine.Name, err.Error())) continue
continue
}
found = true
} }
found = true
} }
} }
if len(errs) > 0 { if len(errs) > 0 {
@ -723,11 +755,11 @@ func (c *Cluster) Networks() cluster.Networks {
} }
// Volumes returns all the volumes in the cluster. // Volumes returns all the volumes in the cluster.
func (c *Cluster) Volumes() []*cluster.Volume { func (c *Cluster) Volumes() cluster.Volumes {
c.RLock() c.RLock()
defer c.RUnlock() defer c.RUnlock()
out := []*cluster.Volume{} out := cluster.Volumes{}
for _, e := range c.engines { for _, e := range c.engines {
out = append(out, e.Volumes()...) out = append(out, e.Volumes()...)
} }
@ -735,26 +767,6 @@ func (c *Cluster) Volumes() []*cluster.Volume {
return out 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. // listNodes returns all validated engines in the cluster, excluding pendingEngines.
func (c *Cluster) listNodes() []*node.Node { func (c *Cluster) listNodes() []*node.Node {
c.RLock() c.RLock()

View File

@ -8,3 +8,43 @@ type Volume struct {
Engine *Engine 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 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)
}
}
// Return if we found a unique match.
if size := len(candidates); size == 1 {
return candidates[0]
} else if size > 1 {
return nil
}
// Match /name and return as soon as we find one.
for _, volume := range volumes {
if volume.Name == "/"+name {
return volume
}
}
if len(candidates) == 1 {
return candidates[0]
}
return nil
}

View File

@ -32,10 +32,11 @@ function teardown() {
swarm_manage swarm_manage
# run # 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 run docker_swarm volume ls -q
[ "${#lines[@]}" -eq 1 ] [ "${#lines[@]}" -eq 1 ]
[[ "${output}" == *"node-0/"* ]]
run docker_swarm volume inspect ${output} run docker_swarm volume inspect ${output}
[ "${#lines[@]}" -eq 7 ] [ "${#lines[@]}" -eq 7 ]
@ -56,6 +57,13 @@ function teardown() {
docker_swarm run -d -v=/tmp busybox true docker_swarm run -d -v=/tmp busybox true
run docker_swarm volume ls run docker_swarm volume ls
[ "${#lines[@]}" -eq 4 ] [ "${#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" { @test "docker volume rm" {