allow engine/name support in volumes

Signed-off-by: Victor Vieux <vieux@docker.com>
This commit is contained in:
Victor Vieux 2016-01-16 13:44:48 -08:00
parent 5cb6f218dd
commit 1225eddc79
8 changed files with 116 additions and 59 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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)
}

View File

@ -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
}

View File

@ -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

View File

@ -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()

View File

@ -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
}

View File

@ -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" {