Browse code

add label support for build, networks and volumes

build: implement --label

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>

network: allow adding labels on create

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>

volume: allow adding labels on create

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>

add tests for build, network, volume

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>

vendor: libnetwork and engine-api bump

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>

Evan Hazlett authored on 2016/03/17 06:52:34
Showing 33 changed files
... ...
@@ -62,6 +62,9 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
62 62
 	cmd.Var(&flBuildArg, []string{"-build-arg"}, "Set build-time variables")
63 63
 	isolation := cmd.String([]string{"-isolation"}, "", "Container isolation technology")
64 64
 
65
+	flLabels := opts.NewListOpts(nil)
66
+	cmd.Var(&flLabels, []string{"-label"}, "Set metadata for an image")
67
+
65 68
 	ulimits := make(map[string]*units.Ulimit)
66 69
 	flUlimits := runconfigopts.NewUlimitOpt(&ulimits)
67 70
 	cmd.Var(flUlimits, []string{"-ulimit"}, "Ulimit options")
... ...
@@ -230,6 +233,7 @@ func (cli *DockerCli) CmdBuild(args ...string) error {
230 230
 		Ulimits:        flUlimits.GetList(),
231 231
 		BuildArgs:      runconfigopts.ConvertKVStringsToMap(flBuildArg.GetAll()),
232 232
 		AuthConfigs:    cli.retrieveAuthConfigs(),
233
+		Labels:         runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
233 234
 	}
234 235
 
235 236
 	response, err := cli.client.ImageBuild(context.Background(), options)
... ...
@@ -44,6 +44,7 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
44 44
 	flIpamGateway := opts.NewListOpts(nil)
45 45
 	flIpamAux := opts.NewMapOpts(nil, nil)
46 46
 	flIpamOpt := opts.NewMapOpts(nil, nil)
47
+	flLabels := opts.NewListOpts(nil)
47 48
 
48 49
 	cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment")
49 50
 	cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
... ...
@@ -51,6 +52,7 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
51 51
 	cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver")
52 52
 	cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options")
53 53
 	cmd.Var(flIpamOpt, []string{"-ipam-opt"}, "set IPAM driver specific options")
54
+	cmd.Var(&flLabels, []string{"-label"}, "set metadata on a network")
54 55
 
55 56
 	flInternal := cmd.Bool([]string{"-internal"}, false, "restricts external access to the network")
56 57
 	flIPv6 := cmd.Bool([]string{"-ipv6"}, false, "enable IPv6 networking")
... ...
@@ -82,6 +84,7 @@ func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
82 82
 		CheckDuplicate: true,
83 83
 		Internal:       *flInternal,
84 84
 		EnableIPv6:     *flIPv6,
85
+		Labels:         runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
85 86
 	}
86 87
 
87 88
 	resp, err := cli.client.NetworkCreate(context.Background(), nc)
... ...
@@ -10,6 +10,7 @@ import (
10 10
 	Cli "github.com/docker/docker/cli"
11 11
 	"github.com/docker/docker/opts"
12 12
 	flag "github.com/docker/docker/pkg/mflag"
13
+	runconfigopts "github.com/docker/docker/runconfig/opts"
13 14
 	"github.com/docker/engine-api/types"
14 15
 	"github.com/docker/engine-api/types/filters"
15 16
 )
... ...
@@ -128,6 +129,9 @@ func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
128 128
 	flDriverOpts := opts.NewMapOpts(nil, nil)
129 129
 	cmd.Var(flDriverOpts, []string{"o", "-opt"}, "Set driver specific options")
130 130
 
131
+	flLabels := opts.NewListOpts(nil)
132
+	cmd.Var(&flLabels, []string{"-label"}, "Set metadata for a volume")
133
+
131 134
 	cmd.Require(flag.Exact, 0)
132 135
 	cmd.ParseFlags(args, true)
133 136
 
... ...
@@ -135,6 +139,7 @@ func (cli *DockerCli) CmdVolumeCreate(args ...string) error {
135 135
 		Driver:     *flDriver,
136 136
 		DriverOpts: flDriverOpts.GetAll(),
137 137
 		Name:       *flName,
138
+		Labels:     runconfigopts.ConvertKVStringsToMap(flLabels.GetAll()),
138 139
 	}
139 140
 
140 141
 	vol, err := cli.client.VolumeCreate(context.Background(), volReq)
... ...
@@ -82,6 +82,15 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
82 82
 		}
83 83
 		options.BuildArgs = buildArgs
84 84
 	}
85
+	var labels = map[string]string{}
86
+	labelsJSON := r.FormValue("labels")
87
+	if labelsJSON != "" {
88
+		if err := json.NewDecoder(strings.NewReader(labelsJSON)).Decode(&labels); err != nil {
89
+			return nil, err
90
+		}
91
+		options.Labels = labels
92
+	}
93
+
85 94
 	return options, nil
86 95
 }
87 96
 
... ...
@@ -12,7 +12,7 @@ type Backend interface {
12 12
 	GetNetworkByName(idName string) (libnetwork.Network, error)
13 13
 	GetNetworksByID(partialID string) []libnetwork.Network
14 14
 	GetAllNetworks() []libnetwork.Network
15
-	CreateNetwork(name, driver string, ipam network.IPAM, options map[string]string, internal bool, enableIPv6 bool) (libnetwork.Network, error)
15
+	CreateNetwork(name, driver string, ipam network.IPAM, options map[string]string, labels map[string]string, internal bool, enableIPv6 bool) (libnetwork.Network, error)
16 16
 	ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
17 17
 	DisconnectContainerFromNetwork(containerName string, network libnetwork.Network, force bool) error
18 18
 	DeleteNetwork(name string) error
... ...
@@ -91,7 +91,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
91 91
 		warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
92 92
 	}
93 93
 
94
-	nw, err = n.backend.CreateNetwork(create.Name, create.Driver, create.IPAM, create.Options, create.Internal, create.EnableIPv6)
94
+	nw, err = n.backend.CreateNetwork(create.Name, create.Driver, create.IPAM, create.Options, create.Labels, create.Internal, create.EnableIPv6)
95 95
 	if err != nil {
96 96
 		return err
97 97
 	}
... ...
@@ -163,16 +163,18 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
163 163
 		return r
164 164
 	}
165 165
 
166
+	info := nw.Info()
166 167
 	r.Name = nw.Name()
167 168
 	r.ID = nw.ID()
168
-	r.Scope = nw.Info().Scope()
169
+	r.Scope = info.Scope()
169 170
 	r.Driver = nw.Type()
170
-	r.EnableIPv6 = nw.Info().IPv6Enabled()
171
-	r.Internal = nw.Info().Internal()
172
-	r.Options = nw.Info().DriverOptions()
171
+	r.EnableIPv6 = info.IPv6Enabled()
172
+	r.Internal = info.Internal()
173
+	r.Options = info.DriverOptions()
173 174
 	r.Containers = make(map[string]types.EndpointResource)
174
-	buildIpamResources(r, nw)
175
-	r.Internal = nw.Info().Internal()
175
+	buildIpamResources(r, info)
176
+	r.Internal = info.Internal()
177
+	r.Labels = info.Labels()
176 178
 
177 179
 	epl := nw.Endpoints()
178 180
 	for _, e := range epl {
... ...
@@ -191,10 +193,10 @@ func buildNetworkResource(nw libnetwork.Network) *types.NetworkResource {
191 191
 	return r
192 192
 }
193 193
 
194
-func buildIpamResources(r *types.NetworkResource, nw libnetwork.Network) {
195
-	id, opts, ipv4conf, ipv6conf := nw.Info().IpamConfig()
194
+func buildIpamResources(r *types.NetworkResource, nwInfo libnetwork.NetworkInfo) {
195
+	id, opts, ipv4conf, ipv6conf := nwInfo.IpamConfig()
196 196
 
197
-	ipv4Info, ipv6Info := nw.Info().IpamInfo()
197
+	ipv4Info, ipv6Info := nwInfo.IpamInfo()
198 198
 
199 199
 	r.IPAM.Driver = id
200 200
 
... ...
@@ -10,7 +10,6 @@ import (
10 10
 type Backend interface {
11 11
 	Volumes(filter string) ([]*types.Volume, []string, error)
12 12
 	VolumeInspect(name string) (*types.Volume, error)
13
-	VolumeCreate(name, driverName string,
14
-		opts map[string]string) (*types.Volume, error)
13
+	VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error)
15 14
 	VolumeRm(name string) error
16 15
 }
... ...
@@ -47,7 +47,7 @@ func (v *volumeRouter) postVolumesCreate(ctx context.Context, w http.ResponseWri
47 47
 		return err
48 48
 	}
49 49
 
50
-	volume, err := v.backend.VolumeCreate(req.Name, req.Driver, req.DriverOpts)
50
+	volume, err := v.backend.VolumeCreate(req.Name, req.Driver, req.DriverOpts, req.Labels)
51 51
 	if err != nil {
52 52
 		return err
53 53
 	}
... ...
@@ -215,6 +215,10 @@ func (b *Builder) build(config *types.ImageBuildOptions, context builder.Context
215 215
 
216 216
 	var shortImgID string
217 217
 	for i, n := range b.dockerfile.Children {
218
+		// we only want to add labels to the last layer
219
+		if i == len(b.dockerfile.Children)-1 {
220
+			b.addLabels()
221
+		}
218 222
 		select {
219 223
 		case <-b.cancelled:
220 224
 			logrus.Debug("Builder: build cancelled!")
... ...
@@ -37,6 +37,19 @@ import (
37 37
 	"github.com/docker/engine-api/types/strslice"
38 38
 )
39 39
 
40
+func (b *Builder) addLabels() {
41
+	// merge labels
42
+	if len(b.options.Labels) > 0 {
43
+		logrus.Debugf("[BUILDER] setting labels %v", b.options.Labels)
44
+		if b.runConfig.Labels == nil {
45
+			b.runConfig.Labels = make(map[string]string)
46
+		}
47
+		for kL, vL := range b.options.Labels {
48
+			b.runConfig.Labels[kL] = vL
49
+		}
50
+	}
51
+}
52
+
40 53
 func (b *Builder) commit(id string, autoCmd strslice.StrSlice, comment string) error {
41 54
 	if b.disableCommit {
42 55
 		return nil
... ...
@@ -45,6 +58,7 @@ func (b *Builder) commit(id string, autoCmd strslice.StrSlice, comment string) e
45 45
 		return fmt.Errorf("Please provide a source image with `from` prior to commit")
46 46
 	}
47 47
 	b.runConfig.Image = b.image
48
+
48 49
 	if id == "" {
49 50
 		cmd := b.runConfig.Cmd
50 51
 		if runtime.GOOS != "windows" {
... ...
@@ -81,6 +95,7 @@ func (b *Builder) commit(id string, autoCmd strslice.StrSlice, comment string) e
81 81
 	if err != nil {
82 82
 		return err
83 83
 	}
84
+
84 85
 	b.image = imageID
85 86
 	return nil
86 87
 }
... ...
@@ -167,12 +167,12 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error {
167 167
 
168 168
 // VolumeCreate creates a volume with the specified name, driver, and opts
169 169
 // This is called directly from the remote API
170
-func (daemon *Daemon) VolumeCreate(name, driverName string, opts map[string]string) (*types.Volume, error) {
170
+func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) {
171 171
 	if name == "" {
172 172
 		name = stringid.GenerateNonCryptoID()
173 173
 	}
174 174
 
175
-	v, err := daemon.volumes.Create(name, driverName, opts)
175
+	v, err := daemon.volumes.Create(name, driverName, opts, labels)
176 176
 	if err != nil {
177 177
 		if volumestore.IsNameConflict(err) {
178 178
 			return nil, fmt.Errorf("A volume named %s already exists. Choose a different volume name.", name)
... ...
@@ -45,7 +45,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
45 45
 			return fmt.Errorf("cannot mount volume over existing file, file exists %s", path)
46 46
 		}
47 47
 
48
-		v, err := daemon.volumes.CreateWithRef(name, hostConfig.VolumeDriver, container.ID, nil)
48
+		v, err := daemon.volumes.CreateWithRef(name, hostConfig.VolumeDriver, container.ID, nil, nil)
49 49
 		if err != nil {
50 50
 			return err
51 51
 		}
... ...
@@ -33,7 +33,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
33 33
 
34 34
 		// Create the volume in the volume driver. If it doesn't exist,
35 35
 		// a new one will be created.
36
-		v, err := daemon.volumes.CreateWithRef(mp.Name, volumeDriver, container.ID, nil)
36
+		v, err := daemon.volumes.CreateWithRef(mp.Name, volumeDriver, container.ID, nil, nil)
37 37
 		if err != nil {
38 38
 			return err
39 39
 		}
... ...
@@ -1498,7 +1498,7 @@ func configureVolumes(config *Config, rootUID, rootGID int) (*store.VolumeStore,
1498 1498
 	}
1499 1499
 
1500 1500
 	volumedrivers.Register(volumesDriver, volumesDriver.Name())
1501
-	return store.New(), nil
1501
+	return store.New(config.Root)
1502 1502
 }
1503 1503
 
1504 1504
 // AuthenticateToRegistry checks the validity of credentials in authConfig
... ...
@@ -118,10 +118,14 @@ func TestGetContainer(t *testing.T) {
118 118
 }
119 119
 
120 120
 func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
121
+	var err error
121 122
 	daemon := &Daemon{
122 123
 		repository: tmp,
123 124
 		root:       tmp,
124
-		volumes:    store.New(),
125
+	}
126
+	daemon.volumes, err = store.New(tmp)
127
+	if err != nil {
128
+		return nil, err
125 129
 	}
126 130
 
127 131
 	volumesDriver, err := local.New(tmp, 0, 0)
... ...
@@ -91,22 +91,23 @@ func (daemon *Daemon) GetAllNetworks() []libnetwork.Network {
91 91
 }
92 92
 
93 93
 // CreateNetwork creates a network with the given name, driver and other optional parameters
94
-func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, netOption map[string]string, internal bool, enableIPv6 bool) (libnetwork.Network, error) {
94
+func (daemon *Daemon) CreateNetwork(name, driver string, ipam network.IPAM, netOption map[string]string, labels map[string]string, internal bool, enableIPv6 bool) (libnetwork.Network, error) {
95 95
 	c := daemon.netController
96 96
 	if driver == "" {
97 97
 		driver = c.Config().Daemon.DefaultDriver
98 98
 	}
99 99
 
100
-	nwOptions := []libnetwork.NetworkOption{}
101
-
102 100
 	v4Conf, v6Conf, err := getIpamConfig(ipam.Config)
103 101
 	if err != nil {
104 102
 		return nil, err
105 103
 	}
106 104
 
107
-	nwOptions = append(nwOptions, libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf, ipam.Options))
108
-	nwOptions = append(nwOptions, libnetwork.NetworkOptionEnableIPv6(enableIPv6))
109
-	nwOptions = append(nwOptions, libnetwork.NetworkOptionDriverOpts(netOption))
105
+	nwOptions := []libnetwork.NetworkOption{
106
+		libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf, ipam.Options),
107
+		libnetwork.NetworkOptionEnableIPv6(enableIPv6),
108
+		libnetwork.NetworkOptionDriverOpts(netOption),
109
+		libnetwork.NetworkOptionLabels(labels),
110
+	}
110 111
 	if internal {
111 112
 		nwOptions = append(nwOptions, libnetwork.NetworkOptionInternalNetwork())
112 113
 	}
... ...
@@ -24,11 +24,17 @@ type mounts []container.Mount
24 24
 
25 25
 // volumeToAPIType converts a volume.Volume to the type used by the remote API
26 26
 func volumeToAPIType(v volume.Volume) *types.Volume {
27
-	return &types.Volume{
27
+	tv := &types.Volume{
28 28
 		Name:       v.Name(),
29 29
 		Driver:     v.DriverName(),
30 30
 		Mountpoint: v.Path(),
31 31
 	}
32
+	if v, ok := v.(interface {
33
+		Labels() map[string]string
34
+	}); ok {
35
+		tv.Labels = v.Labels()
36
+	}
37
+	return tv
32 38
 }
33 39
 
34 40
 // Len returns the number of mounts. Used in sorting.
... ...
@@ -118,7 +124,7 @@ func (daemon *Daemon) registerMountPoints(container *container.Container, hostCo
118 118
 
119 119
 		if len(bind.Name) > 0 {
120 120
 			// create the volume
121
-			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil)
121
+			v, err := daemon.volumes.CreateWithRef(bind.Name, bind.Driver, container.ID, nil, nil)
122 122
 			if err != nil {
123 123
 				return err
124 124
 			}
... ...
@@ -24,12 +24,12 @@ clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://gith
24 24
 clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git
25 25
 clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3
26 26
 clone git github.com/docker/go-connections v0.2.0
27
-clone git github.com/docker/engine-api 68a7b6bebf8f57d559b7788a46c55045438747b9
27
+clone git github.com/docker/engine-api 9524d7ae81ff55771852b6269f40f2a878315de9
28 28
 clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837
29 29
 clone git github.com/imdario/mergo 0.2.1
30 30
 
31 31
 #get libnetwork packages
32
-clone git github.com/docker/libnetwork v0.7.0-dev.8
32
+clone git github.com/docker/libnetwork v0.7.0-dev.9
33 33
 clone git github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
34 34
 clone git github.com/hashicorp/go-msgpack 71c2886f5a673a35f909803f38ece5810165097b
35 35
 clone git github.com/hashicorp/memberlist 9a1e242e454d2443df330bdd51a436d5a9058fc4
... ...
@@ -6596,6 +6596,134 @@ func (s *DockerSuite) TestBuildWorkdirWindowsPath(c *check.C) {
6596 6596
 	}
6597 6597
 }
6598 6598
 
6599
+func (s *DockerSuite) TestBuildLabel(c *check.C) {
6600
+	name := "testbuildlabel"
6601
+	testLabel := "foo"
6602
+
6603
+	_, err := buildImage(name, `
6604
+  FROM `+minimalBaseImage()+`
6605
+  LABEL default foo
6606
+`, false, []string{"--label", testLabel}...)
6607
+
6608
+	if err != nil {
6609
+		c.Fatal("error building image with labels", err)
6610
+	}
6611
+
6612
+	res := inspectFieldJSON(c, name, "Config.Labels")
6613
+
6614
+	var labels map[string]string
6615
+
6616
+	if err := json.Unmarshal([]byte(res), &labels); err != nil {
6617
+		c.Fatal(err)
6618
+	}
6619
+
6620
+	if _, ok := labels[testLabel]; !ok {
6621
+		c.Fatal("label not found in image")
6622
+	}
6623
+}
6624
+
6625
+func (s *DockerSuite) TestBuildLabelCacheCommit(c *check.C) {
6626
+	name := "testbuildlabelcachecommit"
6627
+	testLabel := "foo"
6628
+
6629
+	if _, err := buildImage(name, `
6630
+  FROM `+minimalBaseImage()+`
6631
+  LABEL default foo
6632
+  `, false); err != nil {
6633
+		c.Fatal(err)
6634
+	}
6635
+
6636
+	_, err := buildImage(name, `
6637
+  FROM `+minimalBaseImage()+`
6638
+  LABEL default foo
6639
+`, true, []string{"--label", testLabel}...)
6640
+
6641
+	if err != nil {
6642
+		c.Fatal("error building image with labels", err)
6643
+	}
6644
+
6645
+	res := inspectFieldJSON(c, name, "Config.Labels")
6646
+
6647
+	var labels map[string]string
6648
+
6649
+	if err := json.Unmarshal([]byte(res), &labels); err != nil {
6650
+		c.Fatal(err)
6651
+	}
6652
+
6653
+	if _, ok := labels[testLabel]; !ok {
6654
+		c.Fatal("label not found in image")
6655
+	}
6656
+}
6657
+
6658
+func (s *DockerSuite) TestBuildLabelMultiple(c *check.C) {
6659
+	name := "testbuildlabelmultiple"
6660
+	testLabels := map[string]string{
6661
+		"foo": "bar",
6662
+		"123": "456",
6663
+	}
6664
+
6665
+	labelArgs := []string{}
6666
+
6667
+	for k, v := range testLabels {
6668
+		labelArgs = append(labelArgs, "--label", k+"="+v)
6669
+	}
6670
+
6671
+	_, err := buildImage(name, `
6672
+  FROM `+minimalBaseImage()+`
6673
+  LABEL default foo
6674
+`, false, labelArgs...)
6675
+
6676
+	if err != nil {
6677
+		c.Fatal("error building image with labels", err)
6678
+	}
6679
+
6680
+	res := inspectFieldJSON(c, name, "Config.Labels")
6681
+
6682
+	var labels map[string]string
6683
+
6684
+	if err := json.Unmarshal([]byte(res), &labels); err != nil {
6685
+		c.Fatal(err)
6686
+	}
6687
+
6688
+	for k, v := range testLabels {
6689
+		if x, ok := labels[k]; !ok || x != v {
6690
+			c.Fatalf("label %s=%s not found in image", k, v)
6691
+		}
6692
+	}
6693
+}
6694
+
6695
+func (s *DockerSuite) TestBuildLabelOverwrite(c *check.C) {
6696
+	name := "testbuildlabeloverwrite"
6697
+	testLabel := "foo"
6698
+	testValue := "bar"
6699
+
6700
+	_, err := buildImage(name, `
6701
+  FROM `+minimalBaseImage()+`
6702
+  LABEL `+testLabel+`+ foo
6703
+`, false, []string{"--label", testLabel + "=" + testValue}...)
6704
+
6705
+	if err != nil {
6706
+		c.Fatal("error building image with labels", err)
6707
+	}
6708
+
6709
+	res := inspectFieldJSON(c, name, "Config.Labels")
6710
+
6711
+	var labels map[string]string
6712
+
6713
+	if err := json.Unmarshal([]byte(res), &labels); err != nil {
6714
+		c.Fatal(err)
6715
+	}
6716
+
6717
+	v, ok := labels[testLabel]
6718
+	if !ok {
6719
+		c.Fatal("label not found in image")
6720
+	}
6721
+
6722
+	if v != testValue {
6723
+		c.Fatal("label not overwritten")
6724
+	}
6725
+}
6726
+
6599 6727
 func (s *DockerRegistryAuthHtpasswdSuite) TestBuildFromAuthenticatedRegistry(c *check.C) {
6600 6728
 	dockerCmd(c, "login", "-u", s.reg.username, "-p", s.reg.password, privateRegistryURL)
6601 6729
 
... ...
@@ -341,6 +341,22 @@ func (s *DockerNetworkSuite) TestDockerNetworkCreateDelete(c *check.C) {
341 341
 	assertNwNotAvailable(c, "test")
342 342
 }
343 343
 
344
+func (s *DockerNetworkSuite) TestDockerNetworkCreateLabel(c *check.C) {
345
+	testNet := "testnetcreatelabel"
346
+	testLabel := "foo"
347
+	testValue := "bar"
348
+
349
+	dockerCmd(c, "network", "create", "--label", testLabel+"="+testValue, testNet)
350
+	assertNwIsAvailable(c, testNet)
351
+
352
+	out, _, err := dockerCmdWithError("network", "inspect", "--format='{{ .Labels."+testLabel+" }}'", testNet)
353
+	c.Assert(err, check.IsNil)
354
+	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
355
+
356
+	dockerCmd(c, "network", "rm", testNet)
357
+	assertNwNotAvailable(c, testNet)
358
+}
359
+
344 360
 func (s *DockerSuite) TestDockerNetworkDeleteNotExists(c *check.C) {
345 361
 	out, _, err := dockerCmdWithError("network", "rm", "test")
346 362
 	c.Assert(err, checker.NotNil, check.Commentf("%v", out))
... ...
@@ -241,3 +241,43 @@ func (s *DockerSuite) TestVolumeCliCreateWithOpts(c *check.C) {
241 241
 	}
242 242
 	c.Assert(found, checker.Equals, true)
243 243
 }
244
+
245
+func (s *DockerSuite) TestVolumeCliCreateLabel(c *check.C) {
246
+	testVol := "testvolcreatelabel"
247
+	testLabel := "foo"
248
+	testValue := "bar"
249
+
250
+	out, _, err := dockerCmdWithError("volume", "create", "--label", testLabel+"="+testValue, "--name", testVol)
251
+	c.Assert(err, check.IsNil)
252
+
253
+	out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Labels."+testLabel+" }}'", testVol)
254
+	c.Assert(strings.TrimSpace(out), check.Equals, testValue)
255
+}
256
+
257
+func (s *DockerSuite) TestVolumeCliCreateLabelMultiple(c *check.C) {
258
+	testVol := "testvolcreatelabel"
259
+
260
+	testLabels := map[string]string{
261
+		"foo": "bar",
262
+		"baz": "foo",
263
+	}
264
+
265
+	args := []string{
266
+		"volume",
267
+		"create",
268
+		"--name",
269
+		testVol,
270
+	}
271
+
272
+	for k, v := range testLabels {
273
+		args = append(args, "--label", k+"="+v)
274
+	}
275
+
276
+	out, _, err := dockerCmdWithError(args...)
277
+	c.Assert(err, check.IsNil)
278
+
279
+	for k, v := range testLabels {
280
+		out, _ = dockerCmd(c, "volume", "inspect", "--format='{{ .Labels."+k+" }}'", testVol)
281
+		c.Assert(strings.TrimSpace(out), check.Equals, v)
282
+	}
283
+}
... ...
@@ -101,6 +101,11 @@ func imageBuildOptionsToQuery(options types.ImageBuildOptions) (url.Values, erro
101 101
 	}
102 102
 	query.Set("buildargs", string(buildArgsJSON))
103 103
 
104
+	labelsJSON, err := json.Marshal(options.Labels)
105
+	if err != nil {
106
+		return query, err
107
+	}
108
+	query.Set("labels", string(labelsJSON))
104 109
 	return query, nil
105 110
 }
106 111
 
... ...
@@ -142,6 +142,7 @@ type ImageBuildOptions struct {
142 142
 	BuildArgs      map[string]string
143 143
 	AuthConfigs    map[string]AuthConfig
144 144
 	Context        io.Reader
145
+	Labels         map[string]string
145 146
 }
146 147
 
147 148
 // ImageBuildResponse holds information
... ...
@@ -103,6 +103,13 @@ type GraphDriverData struct {
103 103
 	Data map[string]string
104 104
 }
105 105
 
106
+// RootFS returns Image's RootFS description including the layer IDs.
107
+type RootFS struct {
108
+	Type      string
109
+	Layers    []string `json:",omitempty"`
110
+	BaseLayer string   `json:",omitempty"`
111
+}
112
+
106 113
 // ImageInspect contains response of Remote API:
107 114
 // GET "/images/{name:.*}/json"
108 115
 type ImageInspect struct {
... ...
@@ -122,6 +129,7 @@ type ImageInspect struct {
122 122
 	Size            int64
123 123
 	VirtualSize     int64
124 124
 	GraphDriver     GraphDriverData
125
+	RootFS          RootFS
125 126
 }
126 127
 
127 128
 // Port stores open ports info of container
... ...
@@ -372,6 +380,7 @@ type Volume struct {
372 372
 	Driver     string                 // Driver is the Driver name used to create the volume
373 373
 	Mountpoint string                 // Mountpoint is the location on disk of the volume
374 374
 	Status     map[string]interface{} `json:",omitempty"` // Status provides low-level status information about the volume
375
+	Labels     map[string]string      // Labels is metadata specific to the volume
375 376
 }
376 377
 
377 378
 // VolumesListResponse contains the response for the remote API:
... ...
@@ -387,6 +396,7 @@ type VolumeCreateRequest struct {
387 387
 	Name       string            // Name is the requested name of the volume
388 388
 	Driver     string            // Driver is the name of the driver that should be used to create the volume
389 389
 	DriverOpts map[string]string // DriverOpts holds the driver specific options to use for when creating the volume.
390
+	Labels     map[string]string // Labels holds metadata specific to the volume being created.
390 391
 }
391 392
 
392 393
 // NetworkResource is the body of the "get network" http response message
... ...
@@ -400,6 +410,7 @@ type NetworkResource struct {
400 400
 	Internal   bool
401 401
 	Containers map[string]EndpointResource
402 402
 	Options    map[string]string
403
+	Labels     map[string]string
403 404
 }
404 405
 
405 406
 // EndpointResource contains network resources allocated and used for a container in a network
... ...
@@ -420,6 +431,7 @@ type NetworkCreate struct {
420 420
 	IPAM           network.IPAM
421 421
 	Internal       bool
422 422
 	Options        map[string]string
423
+	Labels         map[string]string
423 424
 }
424 425
 
425 426
 // NetworkCreateResponse is the response message sent by the server for network create call
... ...
@@ -1,5 +1,8 @@
1 1
 # Changelog
2 2
 
3
+## 0.7.0-dev.9 (2016-03-18)
4
+- Support labels on networks
5
+
3 6
 ## 0.7.0-dev.8 (2016-03-16)
4 7
 - Windows driver to respect user set MAC address.
5 8
 - Fix possible nil pointer reference in ServeDNS() with concurrent go routines.
... ...
@@ -1,10 +1,6 @@
1
-FROM golang:1.4-cross
1
+FROM golang:1.5.3
2 2
 RUN apt-get update && apt-get -y install iptables
3 3
 
4
-RUN cd /go/src && mkdir -p golang.org/x && \
5
-    cd golang.org/x && git clone https://github.com/golang/tools && \
6
-    cd tools && git checkout release-branch.go1.5
7
-
8 4
 RUN go get github.com/tools/godep \
9 5
 		github.com/golang/lint/golint \
10 6
 		golang.org/x/tools/cmd/vet \
... ...
@@ -63,6 +63,7 @@ type NetworkInfo interface {
63 63
 	Scope() string
64 64
 	IPv6Enabled() bool
65 65
 	Internal() bool
66
+	Labels() map[string]string
66 67
 }
67 68
 
68 69
 // EndpointWalker is a client provided function which will be used to walk the Endpoints.
... ...
@@ -150,6 +151,7 @@ type network struct {
150 150
 	networkType  string
151 151
 	id           string
152 152
 	scope        string
153
+	labels       map[string]string
153 154
 	ipamType     string
154 155
 	ipamOptions  map[string]string
155 156
 	addrSpace    string
... ...
@@ -309,6 +311,14 @@ func (n *network) CopyTo(o datastore.KVObject) error {
309 309
 	dstN.internal = n.internal
310 310
 	dstN.inDelete = n.inDelete
311 311
 
312
+	// copy labels
313
+	if dstN.labels == nil {
314
+		dstN.labels = make(map[string]string, len(n.labels))
315
+	}
316
+	for k, v := range n.labels {
317
+		dstN.labels[k] = v
318
+	}
319
+
312 320
 	for _, v4conf := range n.ipamV4Config {
313 321
 		dstV4Conf := &IpamConf{}
314 322
 		v4conf.CopyTo(dstV4Conf)
... ...
@@ -359,6 +369,7 @@ func (n *network) MarshalJSON() ([]byte, error) {
359 359
 	netMap["id"] = n.id
360 360
 	netMap["networkType"] = n.networkType
361 361
 	netMap["scope"] = n.scope
362
+	netMap["labels"] = n.labels
362 363
 	netMap["ipamType"] = n.ipamType
363 364
 	netMap["addrSpace"] = n.addrSpace
364 365
 	netMap["enableIPv6"] = n.enableIPv6
... ...
@@ -411,6 +422,15 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
411 411
 	n.networkType = netMap["networkType"].(string)
412 412
 	n.enableIPv6 = netMap["enableIPv6"].(bool)
413 413
 
414
+	// if we weren't unmarshaling to netMap we could simply set n.labels
415
+	// unfortunately, we can't because map[string]interface{} != map[string]string
416
+	if labels, ok := netMap["labels"].(map[string]interface{}); ok {
417
+		n.labels = make(map[string]string, len(labels))
418
+		for label, value := range labels {
419
+			n.labels[label] = value.(string)
420
+		}
421
+	}
422
+
414 423
 	if v, ok := netMap["generic"]; ok {
415 424
 		n.generic = v.(map[string]interface{})
416 425
 		// Restore opts in their map[string]string form
... ...
@@ -539,7 +559,7 @@ func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ip
539 539
 	}
540 540
 }
541 541
 
542
-// NetworkOptionDriverOpts function returns an option setter for any parameter described by a map
542
+// NetworkOptionDriverOpts function returns an option setter for any driver parameter described by a map
543 543
 func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
544 544
 	return func(n *network) {
545 545
 		if n.generic == nil {
... ...
@@ -553,6 +573,13 @@ func NetworkOptionDriverOpts(opts map[string]string) NetworkOption {
553 553
 	}
554 554
 }
555 555
 
556
+// NetworkOptionLabels function returns an option setter for labels specific to a network
557
+func NetworkOptionLabels(labels map[string]string) NetworkOption {
558
+	return func(n *network) {
559
+		n.labels = labels
560
+	}
561
+}
562
+
556 563
 // NetworkOptionDeferIPv6Alloc instructs the network to defer the IPV6 address allocation until after the endpoint has been created
557 564
 // It is being provided to support the specific docker daemon flags where user can deterministically assign an IPv6 address
558 565
 // to a container as combination of fixed-cidr-v6 + mac-address
... ...
@@ -1285,3 +1312,15 @@ func (n *network) IPv6Enabled() bool {
1285 1285
 
1286 1286
 	return n.enableIPv6
1287 1287
 }
1288
+
1289
+func (n *network) Labels() map[string]string {
1290
+	n.Lock()
1291
+	defer n.Unlock()
1292
+
1293
+	var lbls = make(map[string]string, len(n.labels))
1294
+	for k, v := range n.labels {
1295
+		lbls[k] = v
1296
+	}
1297
+
1298
+	return lbls
1299
+}
... ...
@@ -313,8 +313,8 @@ func configureInterface(iface netlink.Link, i *nwIface) error {
313 313
 	}{
314 314
 		{setInterfaceName, fmt.Sprintf("error renaming interface %q to %q", ifaceName, i.DstName())},
315 315
 		{setInterfaceMAC, fmt.Sprintf("error setting interface %q MAC to %q", ifaceName, i.MacAddress())},
316
-		{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %q", ifaceName, i.Address())},
317
-		{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %q", ifaceName, i.AddressIPv6())},
316
+		{setInterfaceIP, fmt.Sprintf("error setting interface %q IP to %v", ifaceName, i.Address())},
317
+		{setInterfaceIPv6, fmt.Sprintf("error setting interface %q IPv6 to %v", ifaceName, i.AddressIPv6())},
318 318
 		{setInterfaceMaster, fmt.Sprintf("error setting interface %q master to %q", ifaceName, i.DstMaster())},
319 319
 	}
320 320
 
... ...
@@ -22,7 +22,8 @@ func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volum
22 22
 	return &volumeAdapter{
23 23
 		proxy:      a.proxy,
24 24
 		name:       name,
25
-		driverName: a.name}, nil
25
+		driverName: a.name,
26
+	}, nil
26 27
 }
27 28
 
28 29
 func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
... ...
@@ -21,7 +21,7 @@ const extName = "VolumeDriver"
21 21
 // NewVolumeDriver returns a driver has the given name mapped on the given client.
22 22
 func NewVolumeDriver(name string, c client) volume.Driver {
23 23
 	proxy := &volumeDriverProxy{c}
24
-	return &volumeDriverAdapter{name, proxy}
24
+	return &volumeDriverAdapter{name: name, proxy: proxy}
25 25
 }
26 26
 
27 27
 type opts map[string]string
... ...
@@ -77,6 +77,10 @@ func New(scope string, rootUID, rootGID int) (*Root, error) {
77 77
 	}
78 78
 
79 79
 	for _, d := range dirs {
80
+		if !d.IsDir() {
81
+			continue
82
+		}
83
+
80 84
 		name := filepath.Base(d.Name())
81 85
 		v := &localVolume{
82 86
 			driverName: r.Name(),
... ...
@@ -198,7 +202,7 @@ func (r *Root) Remove(v volume.Volume) error {
198 198
 
199 199
 	lv, ok := v.(*localVolume)
200 200
 	if !ok {
201
-		return fmt.Errorf("unknown volume type")
201
+		return fmt.Errorf("unknown volume type %T", v)
202 202
 	}
203 203
 
204 204
 	realPath, err := filepath.EvalSymlinks(lv.path)
... ...
@@ -1,22 +1,77 @@
1 1
 package store
2 2
 
3 3
 import (
4
+	"bytes"
5
+	"encoding/json"
6
+	"os"
7
+	"path/filepath"
4 8
 	"sync"
9
+	"time"
5 10
 
6 11
 	"github.com/Sirupsen/logrus"
12
+	"github.com/boltdb/bolt"
7 13
 	"github.com/docker/docker/pkg/locker"
8 14
 	"github.com/docker/docker/volume"
9 15
 	"github.com/docker/docker/volume/drivers"
10 16
 )
11 17
 
18
+const (
19
+	volumeDataDir    = "volumes"
20
+	volumeBucketName = "volumes"
21
+)
22
+
23
+type volumeMetadata struct {
24
+	Name   string
25
+	Labels map[string]string
26
+}
27
+
28
+type volumeWithLabels struct {
29
+	volume.Volume
30
+	labels map[string]string
31
+}
32
+
33
+func (v volumeWithLabels) Labels() map[string]string {
34
+	return v.labels
35
+}
36
+
12 37
 // New initializes a VolumeStore to keep
13 38
 // reference counting of volumes in the system.
14
-func New() *VolumeStore {
15
-	return &VolumeStore{
16
-		locks: &locker.Locker{},
17
-		names: make(map[string]volume.Volume),
18
-		refs:  make(map[string][]string),
39
+func New(rootPath string) (*VolumeStore, error) {
40
+	vs := &VolumeStore{
41
+		locks:  &locker.Locker{},
42
+		names:  make(map[string]volume.Volume),
43
+		refs:   make(map[string][]string),
44
+		labels: make(map[string]map[string]string),
19 45
 	}
46
+
47
+	if rootPath != "" {
48
+		// initialize metadata store
49
+		volPath := filepath.Join(rootPath, volumeDataDir)
50
+		if err := os.MkdirAll(volPath, 750); err != nil {
51
+			return nil, err
52
+		}
53
+
54
+		dbPath := filepath.Join(volPath, "metadata.db")
55
+
56
+		var err error
57
+		vs.db, err = bolt.Open(dbPath, 0600, &bolt.Options{Timeout: 1 * time.Second})
58
+		if err != nil {
59
+			return nil, err
60
+		}
61
+
62
+		// initialize volumes bucket
63
+		if err := vs.db.Update(func(tx *bolt.Tx) error {
64
+			if _, err := tx.CreateBucketIfNotExists([]byte(volumeBucketName)); err != nil {
65
+				return err
66
+			}
67
+
68
+			return nil
69
+		}); err != nil {
70
+			return nil, err
71
+		}
72
+	}
73
+
74
+	return vs, nil
20 75
 }
21 76
 
22 77
 func (s *VolumeStore) getNamed(name string) (volume.Volume, bool) {
... ...
@@ -39,6 +94,7 @@ func (s *VolumeStore) purge(name string) {
39 39
 	s.globalLock.Lock()
40 40
 	delete(s.names, name)
41 41
 	delete(s.refs, name)
42
+	delete(s.labels, name)
42 43
 	s.globalLock.Unlock()
43 44
 }
44 45
 
... ...
@@ -51,6 +107,9 @@ type VolumeStore struct {
51 51
 	names map[string]volume.Volume
52 52
 	// refs stores the volume name and the list of things referencing it
53 53
 	refs map[string][]string
54
+	// labels stores volume labels for each volume
55
+	labels map[string]map[string]string
56
+	db     *bolt.DB
54 57
 }
55 58
 
56 59
 // List proxies to all registered volume drivers to get the full list of volumes
... ...
@@ -137,12 +196,12 @@ func (s *VolumeStore) list() ([]volume.Volume, []string, error) {
137 137
 // CreateWithRef creates a volume with the given name and driver and stores the ref
138 138
 // This is just like Create() except we store the reference while holding the lock.
139 139
 // This ensures there's no race between creating a volume and then storing a reference.
140
-func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts map[string]string) (volume.Volume, error) {
140
+func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts, labels map[string]string) (volume.Volume, error) {
141 141
 	name = normaliseVolumeName(name)
142 142
 	s.locks.Lock(name)
143 143
 	defer s.locks.Unlock(name)
144 144
 
145
-	v, err := s.create(name, driverName, opts)
145
+	v, err := s.create(name, driverName, opts, labels)
146 146
 	if err != nil {
147 147
 		return nil, &OpErr{Err: err, Name: name, Op: "create"}
148 148
 	}
... ...
@@ -152,12 +211,12 @@ func (s *VolumeStore) CreateWithRef(name, driverName, ref string, opts map[strin
152 152
 }
153 153
 
154 154
 // Create creates a volume with the given name and driver.
155
-func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (volume.Volume, error) {
155
+func (s *VolumeStore) Create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
156 156
 	name = normaliseVolumeName(name)
157 157
 	s.locks.Lock(name)
158 158
 	defer s.locks.Unlock(name)
159 159
 
160
-	v, err := s.create(name, driverName, opts)
160
+	v, err := s.create(name, driverName, opts, labels)
161 161
 	if err != nil {
162 162
 		return nil, &OpErr{Err: err, Name: name, Op: "create"}
163 163
 	}
... ...
@@ -169,7 +228,7 @@ func (s *VolumeStore) Create(name, driverName string, opts map[string]string) (v
169 169
 // If a volume with the name is already known, it will ask the stored driver for the volume.
170 170
 // If the passed in driver name does not match the driver name which is stored for the given volume name, an error is returned.
171 171
 // It is expected that callers of this function hold any necessary locks.
172
-func (s *VolumeStore) create(name, driverName string, opts map[string]string) (volume.Volume, error) {
172
+func (s *VolumeStore) create(name, driverName string, opts, labels map[string]string) (volume.Volume, error) {
173 173
 	// Validate the name in a platform-specific manner
174 174
 	valid, err := volume.IsVolumeNameValid(name)
175 175
 	if err != nil {
... ...
@@ -205,7 +264,33 @@ func (s *VolumeStore) create(name, driverName string, opts map[string]string) (v
205 205
 	if v, _ := vd.Get(name); v != nil {
206 206
 		return v, nil
207 207
 	}
208
-	return vd.Create(name, opts)
208
+	v, err := vd.Create(name, opts)
209
+	if err != nil {
210
+		return nil, err
211
+	}
212
+	s.labels[name] = labels
213
+
214
+	if s.db != nil {
215
+		metadata := &volumeMetadata{
216
+			Name:   name,
217
+			Labels: labels,
218
+		}
219
+
220
+		volData, err := json.Marshal(metadata)
221
+		if err != nil {
222
+			return nil, err
223
+		}
224
+
225
+		if err := s.db.Update(func(tx *bolt.Tx) error {
226
+			b := tx.Bucket([]byte(volumeBucketName))
227
+			err := b.Put([]byte(name), volData)
228
+			return err
229
+		}); err != nil {
230
+			return nil, err
231
+		}
232
+	}
233
+
234
+	return volumeWithLabels{v, labels}, nil
209 235
 }
210 236
 
211 237
 // GetWithRef gets a volume with the given name from the passed in driver and stores the ref
... ...
@@ -227,6 +312,9 @@ func (s *VolumeStore) GetWithRef(name, driverName, ref string) (volume.Volume, e
227 227
 	}
228 228
 
229 229
 	s.setNamed(v, ref)
230
+	if labels, ok := s.labels[name]; ok {
231
+		return volumeWithLabels{v, labels}, nil
232
+	}
230 233
 	return v, nil
231 234
 }
232 235
 
... ...
@@ -248,13 +336,43 @@ func (s *VolumeStore) Get(name string) (volume.Volume, error) {
248 248
 // if the driver is unknown it probes all drivers until it finds the first volume with that name.
249 249
 // it is expected that callers of this function hold any necessary locks
250 250
 func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
251
+	labels := map[string]string{}
252
+
253
+	if s.db != nil {
254
+		// get meta
255
+		if err := s.db.Update(func(tx *bolt.Tx) error {
256
+			b := tx.Bucket([]byte(volumeBucketName))
257
+			data := b.Get([]byte(name))
258
+
259
+			if string(data) == "" {
260
+				return nil
261
+			}
262
+
263
+			var meta volumeMetadata
264
+			buf := bytes.NewBuffer(data)
265
+
266
+			if err := json.NewDecoder(buf).Decode(&meta); err != nil {
267
+				return err
268
+			}
269
+			labels = meta.Labels
270
+
271
+			return nil
272
+		}); err != nil {
273
+			return nil, err
274
+		}
275
+	}
276
+
251 277
 	logrus.Debugf("Getting volume reference for name: %s", name)
252 278
 	if v, exists := s.names[name]; exists {
253 279
 		vd, err := volumedrivers.GetDriver(v.DriverName())
254 280
 		if err != nil {
255 281
 			return nil, err
256 282
 		}
257
-		return vd.Get(name)
283
+		vol, err := vd.Get(name)
284
+		if err != nil {
285
+			return nil, err
286
+		}
287
+		return volumeWithLabels{vol, labels}, nil
258 288
 	}
259 289
 
260 290
 	logrus.Debugf("Probing all drivers for volume with name: %s", name)
... ...
@@ -268,7 +386,8 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
268 268
 		if err != nil {
269 269
 			continue
270 270
 		}
271
-		return v, nil
271
+
272
+		return volumeWithLabels{v, labels}, nil
272 273
 	}
273 274
 	return nil, errNoSuchVolume
274 275
 }
... ...
@@ -289,7 +408,8 @@ func (s *VolumeStore) Remove(v volume.Volume) error {
289 289
 	}
290 290
 
291 291
 	logrus.Debugf("Removing volume reference: driver %s, name %s", v.DriverName(), name)
292
-	if err := vd.Remove(v); err != nil {
292
+	vol := withoutLabels(v)
293
+	if err := vd.Remove(vol); err != nil {
293 294
 		return &OpErr{Err: err, Name: name, Op: "remove"}
294 295
 	}
295 296
 
... ...
@@ -372,3 +492,11 @@ func (s *VolumeStore) filter(vols []volume.Volume, f filterFunc) []volume.Volume
372 372
 	}
373 373
 	return ls
374 374
 }
375
+
376
+func withoutLabels(v volume.Volume) volume.Volume {
377
+	if vol, ok := v.(volumeWithLabels); ok {
378
+		return vol.Volume
379
+	}
380
+
381
+	return v
382
+}
... ...
@@ -12,8 +12,11 @@ import (
12 12
 func TestCreate(t *testing.T) {
13 13
 	volumedrivers.Register(vt.NewFakeDriver("fake"), "fake")
14 14
 	defer volumedrivers.Unregister("fake")
15
-	s := New()
16
-	v, err := s.Create("fake1", "fake", nil)
15
+	s, err := New("")
16
+	if err != nil {
17
+		t.Fatal(err)
18
+	}
19
+	v, err := s.Create("fake1", "fake", nil, nil)
17 20
 	if err != nil {
18 21
 		t.Fatal(err)
19 22
 	}
... ...
@@ -24,11 +27,11 @@ func TestCreate(t *testing.T) {
24 24
 		t.Fatalf("Expected 1 volume in the store, got %v: %v", len(l), l)
25 25
 	}
26 26
 
27
-	if _, err := s.Create("none", "none", nil); err == nil {
27
+	if _, err := s.Create("none", "none", nil, nil); err == nil {
28 28
 		t.Fatalf("Expected unknown driver error, got nil")
29 29
 	}
30 30
 
31
-	_, err = s.Create("fakeerror", "fake", map[string]string{"error": "create error"})
31
+	_, err = s.Create("fakeerror", "fake", map[string]string{"error": "create error"}, nil)
32 32
 	expected := &OpErr{Op: "create", Name: "fakeerror", Err: errors.New("create error")}
33 33
 	if err != nil && err.Error() != expected.Error() {
34 34
 		t.Fatalf("Expected create fakeError: create error, got %v", err)
... ...
@@ -40,7 +43,10 @@ func TestRemove(t *testing.T) {
40 40
 	volumedrivers.Register(vt.NewFakeDriver("noop"), "noop")
41 41
 	defer volumedrivers.Unregister("fake")
42 42
 	defer volumedrivers.Unregister("noop")
43
-	s := New()
43
+	s, err := New("")
44
+	if err != nil {
45
+		t.Fatal(err)
46
+	}
44 47
 
45 48
 	// doing string compare here since this error comes directly from the driver
46 49
 	expected := "no such volume"
... ...
@@ -48,7 +54,7 @@ func TestRemove(t *testing.T) {
48 48
 		t.Fatalf("Expected error %q, got %v", expected, err)
49 49
 	}
50 50
 
51
-	v, err := s.CreateWithRef("fake1", "fake", "fake", nil)
51
+	v, err := s.CreateWithRef("fake1", "fake", "fake", nil, nil)
52 52
 	if err != nil {
53 53
 		t.Fatal(err)
54 54
 	}
... ...
@@ -71,11 +77,14 @@ func TestList(t *testing.T) {
71 71
 	defer volumedrivers.Unregister("fake")
72 72
 	defer volumedrivers.Unregister("fake2")
73 73
 
74
-	s := New()
75
-	if _, err := s.Create("test", "fake", nil); err != nil {
74
+	s, err := New("")
75
+	if err != nil {
76
+		t.Fatal(err)
77
+	}
78
+	if _, err := s.Create("test", "fake", nil, nil); err != nil {
76 79
 		t.Fatal(err)
77 80
 	}
78
-	if _, err := s.Create("test2", "fake2", nil); err != nil {
81
+	if _, err := s.Create("test2", "fake2", nil, nil); err != nil {
79 82
 		t.Fatal(err)
80 83
 	}
81 84
 
... ...
@@ -88,7 +97,10 @@ func TestList(t *testing.T) {
88 88
 	}
89 89
 
90 90
 	// and again with a new store
91
-	s = New()
91
+	s, err = New("")
92
+	if err != nil {
93
+		t.Fatal(err)
94
+	}
92 95
 	ls, _, err = s.List()
93 96
 	if err != nil {
94 97
 		t.Fatal(err)
... ...
@@ -103,15 +115,18 @@ func TestFilterByDriver(t *testing.T) {
103 103
 	volumedrivers.Register(vt.NewFakeDriver("noop"), "noop")
104 104
 	defer volumedrivers.Unregister("fake")
105 105
 	defer volumedrivers.Unregister("noop")
106
-	s := New()
106
+	s, err := New("")
107
+	if err != nil {
108
+		t.Fatal(err)
109
+	}
107 110
 
108
-	if _, err := s.Create("fake1", "fake", nil); err != nil {
111
+	if _, err := s.Create("fake1", "fake", nil, nil); err != nil {
109 112
 		t.Fatal(err)
110 113
 	}
111
-	if _, err := s.Create("fake2", "fake", nil); err != nil {
114
+	if _, err := s.Create("fake2", "fake", nil, nil); err != nil {
112 115
 		t.Fatal(err)
113 116
 	}
114
-	if _, err := s.Create("fake3", "noop", nil); err != nil {
117
+	if _, err := s.Create("fake3", "noop", nil, nil); err != nil {
115 118
 		t.Fatal(err)
116 119
 	}
117 120
 
... ...
@@ -128,11 +143,15 @@ func TestFilterByUsed(t *testing.T) {
128 128
 	volumedrivers.Register(vt.NewFakeDriver("fake"), "fake")
129 129
 	volumedrivers.Register(vt.NewFakeDriver("noop"), "noop")
130 130
 
131
-	s := New()
132
-	if _, err := s.CreateWithRef("fake1", "fake", "volReference", nil); err != nil {
131
+	s, err := New("")
132
+	if err != nil {
133 133
 		t.Fatal(err)
134 134
 	}
135
-	if _, err := s.Create("fake2", "fake", nil); err != nil {
135
+
136
+	if _, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil); err != nil {
137
+		t.Fatal(err)
138
+	}
139
+	if _, err := s.Create("fake2", "fake", nil, nil); err != nil {
136 140
 		t.Fatal(err)
137 141
 	}
138 142
 
... ...
@@ -161,8 +180,12 @@ func TestFilterByUsed(t *testing.T) {
161 161
 func TestDerefMultipleOfSameRef(t *testing.T) {
162 162
 	volumedrivers.Register(vt.NewFakeDriver("fake"), "fake")
163 163
 
164
-	s := New()
165
-	v, err := s.CreateWithRef("fake1", "fake", "volReference", nil)
164
+	s, err := New("")
165
+	if err != nil {
166
+		t.Fatal(err)
167
+	}
168
+
169
+	v, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil)
166 170
 	if err != nil {
167 171
 		t.Fatal(err)
168 172
 	}