diff --git a/integration/docker_compose_test.go b/integration/docker_compose_test.go new file mode 100644 index 000000000..cc5ab2c08 --- /dev/null +++ b/integration/docker_compose_test.go @@ -0,0 +1,78 @@ +package integration + +import ( + "encoding/json" + "io/ioutil" + "net/http" + "os" + "time" + + "github.com/containous/traefik/integration/try" + "github.com/containous/traefik/testhelpers" + "github.com/containous/traefik/types" + "github.com/go-check/check" + checker "github.com/vdemeester/shakers" +) + +const ( + composeProject = "minimal" +) + +// Docker test suites +type DockerComposeSuite struct { + BaseSuite +} + +func (s *DockerComposeSuite) SetUpSuite(c *check.C) { + s.createComposeProject(c, composeProject) + s.composeProject.Start(c) +} + +func (s *DockerComposeSuite) TearDownSuite(c *check.C) { + // shutdown and delete compose project + if s.composeProject != nil { + s.composeProject.Stop(c) + } +} + +func (s *DockerComposeSuite) TestComposeScale(c *check.C) { + var serviceCount = 2 + var composeService = "whoami1" + + s.composeProject.Scale(c, composeService, serviceCount) + + file := s.adaptFileForHost(c, "fixtures/docker/simple.toml") + defer os.Remove(file) + + cmd, display := s.traefikCmd(withConfigFile(file)) + defer display(c) + err := cmd.Start() + c.Assert(err, checker.IsNil) + defer cmd.Process.Kill() + + req := testhelpers.MustNewRequest(http.MethodGet, "http://127.0.0.1:8000/whoami", nil) + req.Host = "my.super.host" + + _, err = try.ResponseUntilStatusCode(req, 1500*time.Millisecond, http.StatusOK) + c.Assert(err, checker.IsNil) + + resp, err := http.Get("http://127.0.0.1:8080/api/providers/docker") + c.Assert(err, checker.IsNil) + + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + c.Assert(err, checker.IsNil) + + var provider types.Configuration + c.Assert(json.Unmarshal(body, &provider), checker.IsNil) + + // check that we have only one backend with n servers + c.Assert(provider.Backends, checker.HasLen, 1) + + myBackend := provider.Backends["backend-"+composeService+"-integrationtest"+composeProject] + c.Assert(myBackend, checker.NotNil) + c.Assert(myBackend.Servers, checker.HasLen, serviceCount) + + // check that we have only one frontend + c.Assert(provider.Frontends, checker.HasLen, 1) +} diff --git a/integration/resources/compose/minimal.yml b/integration/resources/compose/minimal.yml new file mode 100644 index 000000000..8490e59a7 --- /dev/null +++ b/integration/resources/compose/minimal.yml @@ -0,0 +1,4 @@ +whoami1: + image: emilevauge/whoami + labels: + - traefik.frontend.rule=PathPrefix:/whoami diff --git a/provider/docker/docker.go b/provider/docker/docker.go index e949e9547..7ddcf8767 100644 --- a/provider/docker/docker.go +++ b/provider/docker/docker.go @@ -369,11 +369,12 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con servers := map[string][]dockerData{} serviceNames := make(map[string]struct{}) for idx, container := range filteredContainers { - if _, exists := serviceNames[container.ServiceName]; !exists { + serviceNameKey := getServiceNameKey(container, p.SwarmMode) + if _, exists := serviceNames[serviceNameKey]; !exists { frontendName := p.getFrontendName(container, idx) frontends[frontendName] = append(frontends[frontendName], container) - if len(container.ServiceName) > 0 { - serviceNames[container.ServiceName] = struct{}{} + if len(serviceNameKey) > 0 { + serviceNames[serviceNameKey] = struct{}{} } } backendName := getBackend(container) @@ -403,6 +404,16 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con return configuration } +func getServiceNameKey(container dockerData, swarmMode bool) string { + serviceNameKey := container.ServiceName + + if len(container.Labels[labelDockerComposeProject]) > 0 && len(container.Labels[labelDockerComposeService]) > 0 && !swarmMode { + serviceNameKey = container.Labels[labelDockerComposeService] + container.Labels[labelDockerComposeProject] + } + + return serviceNameKey +} + // Regexp used to extract the name of the service and the name of the property for this service // All properties are under the format traefik..frontent.*= except the port/weight/protocol directly after traefik.. var servicesPropertiesRegexp = regexp.MustCompile(`^traefik\.(?P.+?)\.(?Pport|weight|protocol|frontend\.(.*))$`) diff --git a/provider/docker/docker_test.go b/provider/docker/docker_test.go index f45ecb14b..f7de92acb 100644 --- a/provider/docker/docker_test.go +++ b/provider/docker/docker_test.go @@ -852,6 +852,94 @@ func TestDockerLoadDockerConfig(t *testing.T) { }, }, }, + { + containers: []docker.ContainerJSON{ + containerJSON( + name("test_0"), + labels(map[string]string{ + labelDockerComposeProject: "myProject", + labelDockerComposeService: "myService", + }), + ports(nat.PortMap{ + "80/tcp": {}, + }), + withNetwork("bridge", ipv4("127.0.0.1")), + ), + containerJSON( + name("test_1"), + labels(map[string]string{ + labelDockerComposeProject: "myProject", + labelDockerComposeService: "myService", + }), + + ports(nat.PortMap{ + "80/tcp": {}, + }), + + withNetwork("bridge", ipv4("127.0.0.2")), + ), + containerJSON( + name("test_2"), + labels(map[string]string{ + labelDockerComposeProject: "myProject", + labelDockerComposeService: "myService2", + }), + + ports(nat.PortMap{ + "80/tcp": {}, + }), + + withNetwork("bridge", ipv4("127.0.0.3")), + ), + }, + expectedFrontends: map[string]*types.Frontend{ + "frontend-Host-myService-myProject-docker-localhost-0": { + Backend: "backend-myService-myProject", + PassHostHeader: true, + EntryPoints: []string{}, + BasicAuth: []string{}, + Routes: map[string]types.Route{ + "route-frontend-Host-myService-myProject-docker-localhost-0": { + Rule: "Host:myService.myProject.docker.localhost", + }, + }, + }, + "frontend-Host-myService2-myProject-docker-localhost-2": { + Backend: "backend-myService2-myProject", + PassHostHeader: true, + EntryPoints: []string{}, + BasicAuth: []string{}, + Routes: map[string]types.Route{ + "route-frontend-Host-myService2-myProject-docker-localhost-2": { + Rule: "Host:myService2.myProject.docker.localhost", + }, + }, + }, + }, + expectedBackends: map[string]*types.Backend{ + "backend-myService2-myProject": { + Servers: map[string]types.Server{ + "server-test_2": { + URL: "http://127.0.0.3:80", + Weight: 0, + }, + }, + CircuitBreaker: nil, + }, + "backend-myService-myProject": { + Servers: map[string]types.Server{ + "server-test_0": { + URL: "http://127.0.0.1:80", + Weight: 0, + }, "server-test_1": { + URL: "http://127.0.0.2:80", + Weight: 0, + }, + }, + CircuitBreaker: nil, + }, + }, + }, } for caseID, test := range testCases {