From d6ad7e2e6491266c4327bbe0394693f790163582 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 1 Dec 2017 14:34:03 +0100 Subject: [PATCH] Fix empty IP for backend when dnsrr in Docker swarm mode --- provider/docker/docker.go | 29 +++++---- provider/docker/swarm_test.go | 118 ++++++++++++++++++++++++++++++++++ 2 files changed, 133 insertions(+), 14 deletions(-) diff --git a/provider/docker/docker.go b/provider/docker/docker.go index 063d34e37..e13c79412 100644 --- a/provider/docker/docker.go +++ b/provider/docker/docker.go @@ -749,7 +749,7 @@ func listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerD networkMap := make(map[string]*dockertypes.NetworkResource) if err != nil { - log.Debug("Failed to network inspect on client for docker, error: %s", err) + log.Debugf("Failed to network inspect on client for docker, error: %s", err) return []dockerData{}, err } for _, network := range networkList { @@ -762,16 +762,18 @@ func listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerD for _, service := range serviceList { dockerData := parseService(service, networkMap) - useSwarmLB, _ := strconv.ParseBool(getIsBackendLBSwarm(dockerData)) - isGlobalSvc := service.Spec.Mode.Global != nil + if len(dockerData.NetworkSettings.Networks) > 0 { + useSwarmLB, _ := strconv.ParseBool(getIsBackendLBSwarm(dockerData)) - if useSwarmLB { - dockerDataList = append(dockerDataList, dockerData) - } else { - dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dockerData, networkMap, isGlobalSvc) + if useSwarmLB { + dockerDataList = append(dockerDataList, dockerData) + } else { + isGlobalSvc := service.Spec.Mode.Global != nil + dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dockerData, networkMap, isGlobalSvc) - for _, dockerDataTask := range dockerDataListTasks { - dockerDataList = append(dockerDataList, dockerDataTask) + for _, dockerDataTask := range dockerDataListTasks { + dockerDataList = append(dockerDataList, dockerDataTask) + } } } } @@ -787,10 +789,9 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes } if service.Spec.EndpointSpec != nil { - switch service.Spec.EndpointSpec.Mode { - case swarmtypes.ResolutionModeDNSRR: - log.Debug("Ignored endpoint-mode not supported, service name: %s", dockerData.Name) - case swarmtypes.ResolutionModeVIP: + if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeDNSRR { + log.Warnf("Ignored endpoint-mode not supported, service name: %s", service.Spec.Annotations.Name) + } else if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeVIP { dockerData.NetworkSettings.Networks = make(map[string]*networkData) for _, virtualIP := range service.Endpoint.VirtualIPs { networkService := networkMap[virtualIP.NetworkID] @@ -803,7 +804,7 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes } dockerData.NetworkSettings.Networks[network.Name] = network } else { - log.Debug("Network not found, id: %s", virtualIP.NetworkID) + log.Debugf("Network not found, id: %s", virtualIP.NetworkID) } } } diff --git a/provider/docker/swarm_test.go b/provider/docker/swarm_test.go index 374e5a6ee..32f95cf74 100644 --- a/provider/docker/swarm_test.go +++ b/provider/docker/swarm_test.go @@ -5,6 +5,7 @@ import ( "strconv" "strings" "testing" + "time" "github.com/containous/traefik/types" "github.com/davecgh/go-spew/spew" @@ -12,6 +13,7 @@ import ( dockertypes "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/swarm" dockerclient "github.com/docker/docker/client" + "github.com/stretchr/testify/assert" "golang.org/x/net/context" ) @@ -765,3 +767,119 @@ func TestListTasks(t *testing.T) { }) } } + +type fakeServicesClient struct { + dockerclient.APIClient + dockerVersion string + networks []dockertypes.NetworkResource + services []swarm.Service + err error +} + +func (c *fakeServicesClient) ServiceList(ctx context.Context, options dockertypes.ServiceListOptions) ([]swarm.Service, error) { + return c.services, c.err +} + +func (c *fakeServicesClient) ServerVersion(ctx context.Context) (dockertypes.Version, error) { + return dockertypes.Version{APIVersion: c.dockerVersion}, c.err +} + +func (c *fakeServicesClient) NetworkList(ctx context.Context, options dockertypes.NetworkListOptions) ([]dockertypes.NetworkResource, error) { + return c.networks, c.err +} + +func TestListServices(t *testing.T) { + testCases := []struct { + desc string + services []swarm.Service + dockerVersion string + networks []dockertypes.NetworkResource + expectedServices []string + }{ + { + desc: "Should return no service due to no networks defined", + services: []swarm.Service{ + swarmService( + serviceName("service1"), + serviceLabels(map[string]string{ + labelDockerNetwork: "barnet", + labelBackendLoadBalancerSwarm: "true", + }), + withEndpointSpec(modeVIP), + withEndpoint( + virtualIP("1", "10.11.12.13/24"), + virtualIP("2", "10.11.12.99/24"), + )), + swarmService( + serviceName("service2"), + serviceLabels(map[string]string{ + labelDockerNetwork: "barnet", + }), + withEndpointSpec(modeDNSSR)), + }, + dockerVersion: "1.30", + networks: []dockertypes.NetworkResource{}, + expectedServices: []string{}, + }, + { + desc: "Should return only service1", + services: []swarm.Service{ + swarmService( + serviceName("service1"), + serviceLabels(map[string]string{ + labelDockerNetwork: "barnet", + labelBackendLoadBalancerSwarm: "true", + }), + withEndpointSpec(modeVIP), + withEndpoint( + virtualIP("yk6l57rfwizjzxxzftn4amaot", "10.11.12.13/24"), + virtualIP("2", "10.11.12.99/24"), + )), + swarmService( + serviceName("service2"), + serviceLabels(map[string]string{ + labelDockerNetwork: "barnet", + }), + withEndpointSpec(modeDNSSR)), + }, + dockerVersion: "1.30", + networks: []dockertypes.NetworkResource{ + { + Name: "network_name", + ID: "yk6l57rfwizjzxxzftn4amaot", + Created: time.Now(), + Scope: "swarm", + Driver: "overlay", + EnableIPv6: false, + Internal: true, + Ingress: false, + ConfigOnly: false, + Options: map[string]string{ + "com.docker.network.driver.overlay.vxlanid_list": "4098", + "com.docker.network.enable_ipv6": "false", + }, + Labels: map[string]string{ + "com.docker.stack.namespace": "test", + }, + }, + }, + expectedServices: []string{ + "service1", + }, + }, + } + + for caseID, test := range testCases { + test := test + t.Run(strconv.Itoa(caseID), func(t *testing.T) { + t.Parallel() + dockerClient := &fakeServicesClient{services: test.services, dockerVersion: test.dockerVersion, networks: test.networks} + serviceDockerData, _ := listServices(context.Background(), dockerClient) + + assert.Equal(t, len(test.expectedServices), len(serviceDockerData)) + for i, serviceName := range test.expectedServices { + assert.Equal(t, serviceName, serviceDockerData[i].Name) + } + }) + } +}