If a network is created with a name that matches another
network's ID, the network with that name was masking the
other network's ID.
As a result, it was not possible to remove the network
with a given ID.
This patch changes the order in which networks are
matched to be what we use for other cases;
1. Match on full ID
2. Match on full Name
3. Match on Partial ID
Before this patch:
$ docker network create foo
336717eac9eaa3da6557042a04efc803f7e8862ce6cf96f6b9565265ba5c618b
$ docker network create 336717eac9eaa3da6557042a04efc803f7e8862ce6cf96f6b9565265ba5c618b
4a698333f1197f20224583abce14876d7f25fdfe416a8545927006c315915a2a
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
4a698333f119 336717eac9eaa3da6557042a04efc803f7e8862ce6cf96f6b9565265ba5c618b bridge local
d1e40d43a2c0 bridge bridge local
336717eac9ea foo bridge local
13cf280a1bbf host host local
d9e4c03728a0 none null local
$ docker network rm 336717eac9eaa3da6557042a04efc803f7e8862ce6cf96f6b9565265ba5c618b
4a698333f1197f20224583abce14876d7f25fdfe416a8545927006c315915a2a
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
d1e40d43a2c0 bridge bridge local
336717eac9ea foo bridge local
13cf280a1bbf host host local
d9e4c03728a0 none null local
After this patch:
$ docker network create foo
2d1791a7def4e2a1ef0f6b83c6add333df0bb4ced2f196c584cb64e6bd94b835
$ docker network create 2d1791a7def4e2a1ef0f6b83c6add333df0bb4ced2f196c584cb64e6bd94b835
6cbc749a529cd2d9d3b10566c84e56c4203dd88b67417437b5fc7a6e955dd48f
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
6cbc749a529c 2d1791a7def4e2a1ef0f6b83c6add333df0bb4ced2f196c584cb64e6bd94b835 bridge local
166c943dbeb5 bridge bridge local
2d1791a7def4 foo bridge local
6c45b8aa6d8e host host local
b11c96b51ea7 none null local
$ docker network rm 2d1791a7def4e2a1ef0f6b83c6add333df0bb4ced2f196c584cb64e6bd94b835
2d1791a7def4e2a1ef0f6b83c6add333df0bb4ced2f196c584cb64e6bd94b835
$ docker network ls
NETWORK ID NAME DRIVER SCOPE
6cbc749a529c 2d1791a7def4e2a1ef0f6b83c6add333df0bb4ced2f196c584cb64e6bd94b835 bridge local
166c943dbeb5 bridge bridge local
6c45b8aa6d8e host host local
b11c96b51ea7 none null local
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
... | ... |
@@ -31,18 +31,27 @@ func (daemon *Daemon) NetworkControllerEnabled() bool { |
31 | 31 |
|
32 | 32 |
// FindNetwork function finds a network for a given string that can represent network name or id |
33 | 33 |
func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) { |
34 |
- // Find by Name |
|
35 |
- n, err := daemon.GetNetworkByName(idName) |
|
36 |
- if err != nil && !isNoSuchNetworkError(err) { |
|
37 |
- return nil, err |
|
34 |
+ // 1. match by full ID. |
|
35 |
+ n, err := daemon.GetNetworkByID(idName) |
|
36 |
+ if err == nil || !isNoSuchNetworkError(err) { |
|
37 |
+ return n, err |
|
38 | 38 |
} |
39 | 39 |
|
40 |
- if n != nil { |
|
41 |
- return n, nil |
|
40 |
+ // 2. match by full name |
|
41 |
+ n, err = daemon.GetNetworkByName(idName) |
|
42 |
+ if err == nil || !isNoSuchNetworkError(err) { |
|
43 |
+ return n, err |
|
42 | 44 |
} |
43 | 45 |
|
44 |
- // Find by id |
|
45 |
- return daemon.GetNetworkByID(idName) |
|
46 |
+ // 3. match by ID prefix |
|
47 |
+ list := daemon.GetNetworksByIDPrefix(idName) |
|
48 |
+ if len(list) == 0 { |
|
49 |
+ return nil, errors.WithStack(networkNotFound(idName)) |
|
50 |
+ } |
|
51 |
+ if len(list) > 1 { |
|
52 |
+ return nil, errors.WithStack(invalidIdentifier(idName)) |
|
53 |
+ } |
|
54 |
+ return list[0], nil |
|
46 | 55 |
} |
47 | 56 |
|
48 | 57 |
func isNoSuchNetworkError(err error) bool { |
... | ... |
@@ -50,18 +59,14 @@ func isNoSuchNetworkError(err error) bool { |
50 | 50 |
return ok |
51 | 51 |
} |
52 | 52 |
|
53 |
-// GetNetworkByID function returns a network whose ID begins with the given prefix. |
|
54 |
-// It fails with an error if no matching, or more than one matching, networks are found. |
|
55 |
-func (daemon *Daemon) GetNetworkByID(partialID string) (libnetwork.Network, error) { |
|
56 |
- list := daemon.GetNetworksByID(partialID) |
|
57 |
- |
|
58 |
- if len(list) == 0 { |
|
59 |
- return nil, errors.WithStack(networkNotFound(partialID)) |
|
60 |
- } |
|
61 |
- if len(list) > 1 { |
|
62 |
- return nil, errors.WithStack(invalidIdentifier(partialID)) |
|
53 |
+// GetNetworkByID function returns a network whose ID matches the given ID. |
|
54 |
+// It fails with an error if no matching network is found. |
|
55 |
+func (daemon *Daemon) GetNetworkByID(id string) (libnetwork.Network, error) { |
|
56 |
+ c := daemon.netController |
|
57 |
+ if c == nil { |
|
58 |
+ return nil, libnetwork.ErrNoSuchNetwork(id) |
|
63 | 59 |
} |
64 |
- return list[0], nil |
|
60 |
+ return c.NetworkByID(id) |
|
65 | 61 |
} |
66 | 62 |
|
67 | 63 |
// GetNetworkByName function returns a network for a given network name. |
... | ... |
@@ -77,8 +82,8 @@ func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) |
77 | 77 |
return c.NetworkByName(name) |
78 | 78 |
} |
79 | 79 |
|
80 |
-// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks |
|
81 |
-func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network { |
|
80 |
+// GetNetworksByIDPrefix returns a list of networks whose ID partially matches zero or more networks |
|
81 |
+func (daemon *Daemon) GetNetworksByIDPrefix(partialID string) []libnetwork.Network { |
|
82 | 82 |
c := daemon.netController |
83 | 83 |
if c == nil { |
84 | 84 |
return nil |
... | ... |
@@ -31,6 +31,11 @@ keywords: "API, Docker, rcli, REST, documentation" |
31 | 31 |
* `POST /containers/create` now accepts additional values for the |
32 | 32 |
`HostConfig.IpcMode` property. New values are `private`, `shareable`, |
33 | 33 |
and `none`. |
34 |
+* `DELETE /networks/{id or name}` fixed issue where a `name` equal to another |
|
35 |
+ network's name was able to mask that `id`. If both a network with the given |
|
36 |
+ _name_ exists, and a network with the given _id_, the network with the given |
|
37 |
+ _id_ is now deleted. This change is not versioned, and affects all API versions |
|
38 |
+ if the daemon has this patch. |
|
34 | 39 |
|
35 | 40 |
## v1.31 API changes |
36 | 41 |
|
37 | 42 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,72 @@ |
0 |
+package network |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "context" |
|
4 |
+ "testing" |
|
5 |
+ |
|
6 |
+ "github.com/docker/docker/api/types" |
|
7 |
+ "github.com/docker/docker/integration/util/request" |
|
8 |
+ "github.com/stretchr/testify/assert" |
|
9 |
+ "github.com/stretchr/testify/require" |
|
10 |
+) |
|
11 |
+ |
|
12 |
+func containsNetwork(nws []types.NetworkResource, nw types.NetworkCreateResponse) bool { |
|
13 |
+ for _, n := range nws { |
|
14 |
+ if n.ID == nw.ID { |
|
15 |
+ return true |
|
16 |
+ } |
|
17 |
+ } |
|
18 |
+ return false |
|
19 |
+} |
|
20 |
+ |
|
21 |
+// createAmbiguousNetworks creates three networks, of which the second network |
|
22 |
+// uses a prefix of the first network's ID as name. The third network uses the |
|
23 |
+// first network's ID as name. |
|
24 |
+// |
|
25 |
+// After successful creation, properties of all three networks is returned |
|
26 |
+func createAmbiguousNetworks(t *testing.T) (types.NetworkCreateResponse, types.NetworkCreateResponse, types.NetworkCreateResponse) { |
|
27 |
+ client := request.NewAPIClient(t) |
|
28 |
+ ctx := context.Background() |
|
29 |
+ |
|
30 |
+ testNet, err := client.NetworkCreate(ctx, "testNet", types.NetworkCreate{}) |
|
31 |
+ require.NoError(t, err) |
|
32 |
+ idPrefixNet, err := client.NetworkCreate(ctx, testNet.ID[:12], types.NetworkCreate{}) |
|
33 |
+ require.NoError(t, err) |
|
34 |
+ fullIDNet, err := client.NetworkCreate(ctx, testNet.ID, types.NetworkCreate{}) |
|
35 |
+ require.NoError(t, err) |
|
36 |
+ |
|
37 |
+ nws, err := client.NetworkList(ctx, types.NetworkListOptions{}) |
|
38 |
+ require.NoError(t, err) |
|
39 |
+ |
|
40 |
+ assert.Equal(t, true, containsNetwork(nws, testNet), "failed to create network testNet") |
|
41 |
+ assert.Equal(t, true, containsNetwork(nws, idPrefixNet), "failed to create network idPrefixNet") |
|
42 |
+ assert.Equal(t, true, containsNetwork(nws, fullIDNet), "failed to create network fullIDNet") |
|
43 |
+ return testNet, idPrefixNet, fullIDNet |
|
44 |
+} |
|
45 |
+ |
|
46 |
+// TestDockerNetworkDeletePreferID tests that if a network with a name |
|
47 |
+// equal to another network's ID exists, the Network with the given |
|
48 |
+// ID is removed, and not the network with the given name. |
|
49 |
+func TestDockerNetworkDeletePreferID(t *testing.T) { |
|
50 |
+ defer setupTest(t)() |
|
51 |
+ client := request.NewAPIClient(t) |
|
52 |
+ ctx := context.Background() |
|
53 |
+ testNet, idPrefixNet, fullIDNet := createAmbiguousNetworks(t) |
|
54 |
+ |
|
55 |
+ // Delete the network using a prefix of the first network's ID as name. |
|
56 |
+ // This should the network name with the id-prefix, not the original network. |
|
57 |
+ err := client.NetworkRemove(ctx, testNet.ID[:12]) |
|
58 |
+ require.NoError(t, err) |
|
59 |
+ |
|
60 |
+ // Delete the network using networkID. This should remove the original |
|
61 |
+ // network, not the network with the name equal to the networkID |
|
62 |
+ err = client.NetworkRemove(ctx, testNet.ID) |
|
63 |
+ require.NoError(t, err) |
|
64 |
+ |
|
65 |
+ // networks "testNet" and "idPrefixNet" should be removed, but "fullIDNet" should still exist |
|
66 |
+ nws, err := client.NetworkList(ctx, types.NetworkListOptions{}) |
|
67 |
+ require.NoError(t, err) |
|
68 |
+ assert.Equal(t, false, containsNetwork(nws, testNet), "Network testNet not removed") |
|
69 |
+ assert.Equal(t, false, containsNetwork(nws, idPrefixNet), "Network idPrefixNet not removed") |
|
70 |
+ assert.Equal(t, true, containsNetwork(nws, fullIDNet), "Network fullIDNet not found") |
|
71 |
+} |
0 | 72 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,28 @@ |
0 |
+package network |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "fmt" |
|
4 |
+ "os" |
|
5 |
+ "testing" |
|
6 |
+ |
|
7 |
+ "github.com/docker/docker/internal/test/environment" |
|
8 |
+) |
|
9 |
+ |
|
10 |
+var testEnv *environment.Execution |
|
11 |
+ |
|
12 |
+func TestMain(m *testing.M) { |
|
13 |
+ var err error |
|
14 |
+ testEnv, err = environment.New() |
|
15 |
+ if err != nil { |
|
16 |
+ fmt.Println(err) |
|
17 |
+ os.Exit(1) |
|
18 |
+ } |
|
19 |
+ |
|
20 |
+ testEnv.Print() |
|
21 |
+ os.Exit(m.Run()) |
|
22 |
+} |
|
23 |
+ |
|
24 |
+func setupTest(t *testing.T) func() { |
|
25 |
+ environment.ProtectAll(t, testEnv) |
|
26 |
+ return func() { testEnv.Clean(t) } |
|
27 |
+} |