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)
}
// 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
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)
@ -794,28 +822,6 @@ func ping(c *context, w http.ResponseWriter, r *http.Request) {
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
func proxyNetworkContainerOperation(c *context, w http.ResponseWriter, r *http.Request) {
var networkid = mux.Vars(r)["networkid"]

View File

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

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
@ -67,20 +67,21 @@ func (networks Networks) Get(IDOrName string) *Network {
candidates := []*Network{}
// Match name, /name or engine/name.
// Match name or engine/name.
for _, network := range networks {
if network.Name == IDOrName || network.Engine.ID+"/"+network.Name == IDOrName || network.Engine.Name+"/"+network.Name == IDOrName {
candidates = append(candidates, network)
}
}
// Return if we found a unique match.
if size := len(candidates); size == 1 {
return candidates[0]
} else if size > 1 {
return nil
}
// Match name, /name or engine/name.
// Match /name and return as soon as we find one.
for _, network := range networks {
if network.Name == "/"+IDOrName {
return network

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,12 +484,17 @@ 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)
@ -498,6 +515,23 @@ func (c *Cluster) CreateVolume(request *dockerclient.VolumeCreateRequest) (*clus
c.RUnlock()
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
}
if er != nil && volume == nil {
err = er
}
}
}
return volume, err
}
@ -511,16 +545,14 @@ 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 {
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 {
err = errors.New(strings.Join(errs, "\n"))
}
@ -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,43 @@ 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 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
# 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" {