From 7cf8b20762cc9491f52ff3f3d94c880378183696 Mon Sep 17 00:00:00 2001 From: selansen Date: Wed, 14 Feb 2018 23:23:35 -0500 Subject: [PATCH] Fix to address regression caused by PR 30897 With the inclusion of PR 30897, creating service for host network fails in 18.02. Modified IsPreDefinedNetwork check and return NetworkNameError instead of errdefs.Forbidden to address this issue Signed-off-by: selansen --- daemon/cluster/executor/container/adapter.go | 7 +- daemon/network.go | 15 ++++- integration/network/service_test.go | 70 ++++++++++++++++++++ 3 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 integration/network/service_test.go diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index 7395036cc8..4af6e3dd97 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -18,6 +18,7 @@ import ( containertypes "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/events" containerpkg "github.com/docker/docker/container" + "github.com/docker/docker/daemon" "github.com/docker/docker/daemon/cluster/convert" executorpkg "github.com/docker/docker/daemon/cluster/executor" "github.com/docker/libnetwork" @@ -155,7 +156,11 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error { if _, ok := err.(libnetwork.NetworkNameError); ok { continue } - + // We will continue if CreateManagedNetwork returns PredefinedNetworkError error. + // Other callers still can treat it as Error. + if _, ok := err.(daemon.PredefinedNetworkError); ok { + continue + } return err } } diff --git a/daemon/network.go b/daemon/network.go index 2c2a96b343..e5dcd06862 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -24,6 +24,16 @@ import ( "golang.org/x/net/context" ) +// PredefinedNetworkError is returned when user tries to create predefined network that already exists. +type PredefinedNetworkError string + +func (pnr PredefinedNetworkError) Error() string { + return fmt.Sprintf("operation is not permitted on predefined %s network ", string(pnr)) +} + +// Forbidden denotes the type of this error +func (pnr PredefinedNetworkError) Forbidden() {} + // NetworkControllerEnabled checks if the networking stack is enabled. // This feature depends on OS primitives and it's disabled in systems like Windows. func (daemon *Daemon) NetworkControllerEnabled() bool { @@ -267,9 +277,8 @@ func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.N } func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) { - if runconfig.IsPreDefinedNetwork(create.Name) && !agent { - err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name) - return nil, errdefs.Forbidden(err) + if runconfig.IsPreDefinedNetwork(create.Name) { + return nil, PredefinedNetworkError(create.Name) } var warning string diff --git a/integration/network/service_test.go b/integration/network/service_test.go new file mode 100644 index 0000000000..684b29c1c1 --- /dev/null +++ b/integration/network/service_test.go @@ -0,0 +1,70 @@ +package network // import "github.com/docker/docker/integration/network" + +import ( + "runtime" + "testing" + "time" + + "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/swarm" + "github.com/docker/docker/client" + "github.com/gotestyourself/gotestyourself/poll" + "github.com/stretchr/testify/require" + "golang.org/x/net/context" +) + +func TestServiceWithPredefinedNetwork(t *testing.T) { + defer setupTest(t)() + d := newSwarm(t) + defer d.Stop(t) + client, err := client.NewClientWithOpts(client.WithHost((d.Sock()))) + require.NoError(t, err) + + hostName := "host" + var instances uint64 = 1 + serviceName := "TestService" + serviceSpec := swarmServiceSpec(serviceName, instances) + serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: hostName}) + + serviceResp, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{ + QueryRegistry: false, + }) + require.NoError(t, err) + + pollSettings := func(config *poll.Settings) { + if runtime.GOARCH == "arm64" || runtime.GOARCH == "arm" { + config.Timeout = 50 * time.Second + config.Delay = 100 * time.Millisecond + } + } + + serviceID := serviceResp.ID + poll.WaitOn(t, serviceRunningCount(client, serviceID, instances), pollSettings) + + _, _, err = client.ServiceInspectWithRaw(context.Background(), serviceID, types.ServiceInspectOptions{}) + require.NoError(t, err) + + err = client.ServiceRemove(context.Background(), serviceID) + require.NoError(t, err) + + poll.WaitOn(t, serviceIsRemoved(client, serviceID), pollSettings) + poll.WaitOn(t, noTasks(client), pollSettings) + +} + +func serviceRunningCount(client client.ServiceAPIClient, serviceID string, instances uint64) func(log poll.LogT) poll.Result { + return func(log poll.LogT) poll.Result { + filter := filters.NewArgs() + filter.Add("service", serviceID) + services, err := client.ServiceList(context.Background(), types.ServiceListOptions{}) + if err != nil { + return poll.Error(err) + } + + if len(services) != int(instances) { + return poll.Continue("Service count at %d waiting for %d", len(services), instances) + } + return poll.Success() + } +}