Fix network name masking network ID on delete

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>
This commit is contained in:
Sebastiaan van Stijn 2017-08-14 22:23:57 +02:00
parent 38c5af2c44
commit e52001c56e
No known key found for this signature in database
GPG Key ID: 76698F39D527CE8C
4 changed files with 131 additions and 21 deletions

View File

@ -31,18 +31,27 @@ func (daemon *Daemon) NetworkControllerEnabled() bool {
// FindNetwork function finds a network for a given string that can represent network name or id
func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
// Find by Name
n, err := daemon.GetNetworkByName(idName)
if err != nil && !isNoSuchNetworkError(err) {
return nil, err
// 1. match by full ID.
n, err := daemon.GetNetworkByID(idName)
if err == nil || !isNoSuchNetworkError(err) {
return n, err
}
if n != nil {
return n, nil
// 2. match by full name
n, err = daemon.GetNetworkByName(idName)
if err == nil || !isNoSuchNetworkError(err) {
return n, err
}
// Find by id
return daemon.GetNetworkByID(idName)
// 3. match by ID prefix
list := daemon.GetNetworksByIDPrefix(idName)
if len(list) == 0 {
return nil, errors.WithStack(networkNotFound(idName))
}
if len(list) > 1 {
return nil, errors.WithStack(invalidIdentifier(idName))
}
return list[0], nil
}
func isNoSuchNetworkError(err error) bool {
@ -50,18 +59,14 @@ func isNoSuchNetworkError(err error) bool {
return ok
}
// GetNetworkByID function returns a network whose ID begins with the given prefix.
// It fails with an error if no matching, or more than one matching, networks are found.
func (daemon *Daemon) GetNetworkByID(partialID string) (libnetwork.Network, error) {
list := daemon.GetNetworksByID(partialID)
if len(list) == 0 {
return nil, errors.WithStack(networkNotFound(partialID))
// GetNetworkByID function returns a network whose ID matches the given ID.
// It fails with an error if no matching network is found.
func (daemon *Daemon) GetNetworkByID(id string) (libnetwork.Network, error) {
c := daemon.netController
if c == nil {
return nil, libnetwork.ErrNoSuchNetwork(id)
}
if len(list) > 1 {
return nil, errors.WithStack(invalidIdentifier(partialID))
}
return list[0], nil
return c.NetworkByID(id)
}
// GetNetworkByName function returns a network for a given network name.
@ -77,8 +82,8 @@ func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error)
return c.NetworkByName(name)
}
// GetNetworksByID returns a list of networks whose ID partially matches zero or more networks
func (daemon *Daemon) GetNetworksByID(partialID string) []libnetwork.Network {
// GetNetworksByIDPrefix returns a list of networks whose ID partially matches zero or more networks
func (daemon *Daemon) GetNetworksByIDPrefix(partialID string) []libnetwork.Network {
c := daemon.netController
if c == nil {
return nil

View File

@ -31,6 +31,11 @@ keywords: "API, Docker, rcli, REST, documentation"
* `POST /containers/create` now accepts additional values for the
`HostConfig.IpcMode` property. New values are `private`, `shareable`,
and `none`.
* `DELETE /networks/{id or name}` fixed issue where a `name` equal to another
network's name was able to mask that `id`. If both a network with the given
_name_ exists, and a network with the given _id_, the network with the given
_id_ is now deleted. This change is not versioned, and affects all API versions
if the daemon has this patch.
## v1.31 API changes

View File

@ -0,0 +1,72 @@
package network
import (
"context"
"testing"
"github.com/docker/docker/api/types"
"github.com/docker/docker/integration/util/request"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func containsNetwork(nws []types.NetworkResource, nw types.NetworkCreateResponse) bool {
for _, n := range nws {
if n.ID == nw.ID {
return true
}
}
return false
}
// createAmbiguousNetworks creates three networks, of which the second network
// uses a prefix of the first network's ID as name. The third network uses the
// first network's ID as name.
//
// After successful creation, properties of all three networks is returned
func createAmbiguousNetworks(t *testing.T) (types.NetworkCreateResponse, types.NetworkCreateResponse, types.NetworkCreateResponse) {
client := request.NewAPIClient(t)
ctx := context.Background()
testNet, err := client.NetworkCreate(ctx, "testNet", types.NetworkCreate{})
require.NoError(t, err)
idPrefixNet, err := client.NetworkCreate(ctx, testNet.ID[:12], types.NetworkCreate{})
require.NoError(t, err)
fullIDNet, err := client.NetworkCreate(ctx, testNet.ID, types.NetworkCreate{})
require.NoError(t, err)
nws, err := client.NetworkList(ctx, types.NetworkListOptions{})
require.NoError(t, err)
assert.Equal(t, true, containsNetwork(nws, testNet), "failed to create network testNet")
assert.Equal(t, true, containsNetwork(nws, idPrefixNet), "failed to create network idPrefixNet")
assert.Equal(t, true, containsNetwork(nws, fullIDNet), "failed to create network fullIDNet")
return testNet, idPrefixNet, fullIDNet
}
// TestDockerNetworkDeletePreferID tests that if a network with a name
// equal to another network's ID exists, the Network with the given
// ID is removed, and not the network with the given name.
func TestDockerNetworkDeletePreferID(t *testing.T) {
defer setupTest(t)()
client := request.NewAPIClient(t)
ctx := context.Background()
testNet, idPrefixNet, fullIDNet := createAmbiguousNetworks(t)
// Delete the network using a prefix of the first network's ID as name.
// This should the network name with the id-prefix, not the original network.
err := client.NetworkRemove(ctx, testNet.ID[:12])
require.NoError(t, err)
// Delete the network using networkID. This should remove the original
// network, not the network with the name equal to the networkID
err = client.NetworkRemove(ctx, testNet.ID)
require.NoError(t, err)
// networks "testNet" and "idPrefixNet" should be removed, but "fullIDNet" should still exist
nws, err := client.NetworkList(ctx, types.NetworkListOptions{})
require.NoError(t, err)
assert.Equal(t, false, containsNetwork(nws, testNet), "Network testNet not removed")
assert.Equal(t, false, containsNetwork(nws, idPrefixNet), "Network idPrefixNet not removed")
assert.Equal(t, true, containsNetwork(nws, fullIDNet), "Network fullIDNet not found")
}

View File

@ -0,0 +1,28 @@
package network
import (
"fmt"
"os"
"testing"
"github.com/docker/docker/internal/test/environment"
)
var testEnv *environment.Execution
func TestMain(m *testing.M) {
var err error
testEnv, err = environment.New()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
testEnv.Print()
os.Exit(m.Run())
}
func setupTest(t *testing.T) func() {
environment.ProtectAll(t, testEnv)
return func() { testEnv.Clean(t) }
}