Browse code

api: allow creating a network of which name is the prefix of the ID of a swarm network

Previously, it doesn't allow creating such a network:

e.g.

$ docker network inspect -f '{{.Id}}' ingress
84xh9knigj6zyt00u31e26nj3
$ docker network create 84
Error response from daemon: network with name 84 already exists

Fix #27866

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
(cherry picked from commit edfbc3b8767ab2e89e73ba3142d2ddad295001e9)
Signed-off-by: Victor Vieux <vieux@docker.com>

Akihiro Suda authored on 2016/11/01 14:49:39
Showing 3 changed files
... ...
@@ -80,7 +80,7 @@ func (n *networkRouter) postNetworkCreate(ctx context.Context, w http.ResponseWr
80 80
 		return err
81 81
 	}
82 82
 
83
-	if _, err := n.clusterProvider.GetNetwork(create.Name); err == nil {
83
+	if nws, err := n.clusterProvider.GetNetworksByName(create.Name); err == nil && len(nws) > 0 {
84 84
 		return libnetwork.NetworkNameError(create.Name)
85 85
 	}
86 86
 
... ...
@@ -1556,8 +1556,7 @@ func (c *Cluster) GetNetwork(input string) (apitypes.NetworkResource, error) {
1556 1556
 	return convert.BasicNetworkFromGRPC(*network), nil
1557 1557
 }
1558 1558
 
1559
-// GetNetworks returns all current cluster managed networks.
1560
-func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
1559
+func (c *Cluster) getNetworks(filters *swarmapi.ListNetworksRequest_Filters) ([]apitypes.NetworkResource, error) {
1561 1560
 	c.RLock()
1562 1561
 	defer c.RUnlock()
1563 1562
 
... ...
@@ -1568,7 +1567,7 @@ func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
1568 1568
 	ctx, cancel := c.getRequestContext()
1569 1569
 	defer cancel()
1570 1570
 
1571
-	r, err := c.client.ListNetworks(ctx, &swarmapi.ListNetworksRequest{})
1571
+	r, err := c.client.ListNetworks(ctx, &swarmapi.ListNetworksRequest{Filters: filters})
1572 1572
 	if err != nil {
1573 1573
 		return nil, err
1574 1574
 	}
... ...
@@ -1582,6 +1581,21 @@ func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
1582 1582
 	return networks, nil
1583 1583
 }
1584 1584
 
1585
+// GetNetworks returns all current cluster managed networks.
1586
+func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
1587
+	return c.getNetworks(nil)
1588
+}
1589
+
1590
+// GetNetworksByName returns cluster managed networks by name.
1591
+// It is ok to have multiple networks here. #18864
1592
+func (c *Cluster) GetNetworksByName(name string) ([]apitypes.NetworkResource, error) {
1593
+	// Note that swarmapi.GetNetworkRequest.Name is not functional.
1594
+	// So we cannot just use that with c.GetNetwork.
1595
+	return c.getNetworks(&swarmapi.ListNetworksRequest_Filters{
1596
+		Names: []string{name},
1597
+	})
1598
+}
1599
+
1585 1600
 func attacherKey(target, containerID string) string {
1586 1601
 	return containerID + ":" + target
1587 1602
 }
... ...
@@ -1184,3 +1184,50 @@ func (s *DockerTrustedSwarmSuite) TestTrustedServiceUpdate(c *check.C) {
1184 1184
 	c.Assert(err, check.NotNil, check.Commentf(out))
1185 1185
 	c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
1186 1186
 }
1187
+
1188
+// Test case for issue #27866, which did not allow NW name that is the prefix of a swarm NW ID.
1189
+// e.g. if the ingress ID starts with "n1", it was impossible to create a NW named "n1".
1190
+func (s *DockerSwarmSuite) TestSwarmNetworkCreateIssue27866(c *check.C) {
1191
+	d := s.AddDaemon(c, true, true)
1192
+	out, err := d.Cmd("network", "inspect", "-f", "{{.Id}}", "ingress")
1193
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
1194
+	ingressID := strings.TrimSpace(out)
1195
+	c.Assert(ingressID, checker.Not(checker.Equals), "")
1196
+
1197
+	// create a network of which name is the prefix of the ID of an overlay network
1198
+	// (ingressID in this case)
1199
+	newNetName := ingressID[0:2]
1200
+	out, err = d.Cmd("network", "create", "--driver", "overlay", newNetName)
1201
+	// In #27866, it was failing because of "network with name %s already exists"
1202
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
1203
+	out, err = d.Cmd("network", "rm", newNetName)
1204
+	c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
1205
+}
1206
+
1207
+// Test case for https://github.com/docker/docker/pull/27938#issuecomment-265768303
1208
+// This test creates two networks with the same name sequentially, with various drivers.
1209
+// Since the operations in this test are done sequentially, the 2nd call should fail with
1210
+// "network with name FOO already exists".
1211
+// Note that it is to ok have multiple networks with the same name if the operations are done
1212
+// in parallel. (#18864)
1213
+func (s *DockerSwarmSuite) TestSwarmNetworkCreateDup(c *check.C) {
1214
+	d := s.AddDaemon(c, true, true)
1215
+	drivers := []string{"bridge", "overlay"}
1216
+	for i, driver1 := range drivers {
1217
+		nwName := fmt.Sprintf("network-test-%d", i)
1218
+		for _, driver2 := range drivers {
1219
+			c.Logf("Creating a network named %q with %q, then %q",
1220
+				nwName, driver1, driver2)
1221
+			out, err := d.Cmd("network", "create", "--driver", driver1, nwName)
1222
+			c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
1223
+			out, err = d.Cmd("network", "create", "--driver", driver2, nwName)
1224
+			c.Assert(out, checker.Contains,
1225
+				fmt.Sprintf("network with name %s already exists", nwName))
1226
+			c.Assert(err, checker.NotNil)
1227
+			c.Logf("As expected, the attempt to network %q with %q failed: %s",
1228
+				nwName, driver2, out)
1229
+			out, err = d.Cmd("network", "rm", nwName)
1230
+			c.Assert(err, checker.IsNil, check.Commentf("out: %v", out))
1231
+		}
1232
+	}
1233
+}