1
0
Fork 0

Merge 'v1.4.2' into master

This commit is contained in:
Fernandez Ludovic 2017-11-03 13:51:24 +01:00
commit 2070aa9443
16 changed files with 304 additions and 87 deletions

View file

@ -29,11 +29,12 @@ import (
"github.com/docker/docker/client"
"github.com/docker/go-connections/nat"
"github.com/docker/go-connections/sockets"
"github.com/pkg/errors"
)
const (
// SwarmAPIVersion is a constant holding the version of the Provider API traefik will use
SwarmAPIVersion string = "1.24"
SwarmAPIVersion = "1.24"
// SwarmDefaultWatchTime is the duration of the interval when polling docker
SwarmDefaultWatchTime = 15 * time.Second
@ -306,8 +307,8 @@ func (p *Provider) loadDockerConfig(containersInspected []dockerData) *types.Con
frontends := map[string][]dockerData{}
backends := map[string]dockerData{}
servers := map[string][]dockerData{}
for _, container := range filteredContainers {
frontendName := p.getFrontendName(container)
for idx, container := range filteredContainers {
frontendName := p.getFrontendName(container, idx)
frontends[frontendName] = append(frontends[frontendName], container)
backendName := p.getBackend(container)
backends[backendName] = container
@ -522,9 +523,16 @@ func (p *Provider) getMaxConnExtractorFunc(container dockerData) string {
}
func (p *Provider) containerFilter(container dockerData) bool {
_, err := strconv.Atoi(container.Labels[types.LabelPort])
var err error
portLabel := "traefik.port label"
if p.hasServices(container) {
portLabel = "traefik.<serviceName>.port or " + portLabel + "s"
err = checkServiceLabelPort(container)
} else {
_, err = strconv.Atoi(container.Labels[types.LabelPort])
}
if len(container.NetworkSettings.Ports) == 0 && err != nil {
log.Debugf("Filtering container without port and no traefik.port label %s", container.Name)
log.Debugf("Filtering container without port and no %s %s : %s", portLabel, container.Name, err.Error())
return false
}
@ -554,9 +562,46 @@ func (p *Provider) containerFilter(container dockerData) bool {
return true
}
func (p *Provider) getFrontendName(container dockerData) string {
// checkServiceLabelPort checks if all service names have a port service label
// or if port container label exists for default value
func checkServiceLabelPort(container dockerData) error {
// If port container label is present, there is a default values for all ports, use it for the check
_, err := strconv.Atoi(container.Labels[types.LabelPort])
if err != nil {
serviceLabelPorts := make(map[string]struct{})
serviceLabels := make(map[string]struct{})
portRegexp := regexp.MustCompile(`^traefik\.(?P<service_name>.+?)\.port$`)
for label := range container.Labels {
// Get all port service labels
portLabel := portRegexp.FindStringSubmatch(label)
if portLabel != nil && len(portLabel) > 0 {
serviceLabelPorts[portLabel[0]] = struct{}{}
}
// Get only one instance of all service names from service labels
servicesLabelNames := servicesPropertiesRegexp.FindStringSubmatch(label)
if servicesLabelNames != nil && len(servicesLabelNames) > 0 {
serviceLabels[strings.Split(servicesLabelNames[0], ".")[1]] = struct{}{}
}
}
// If the number of service labels is different than the number of port services label
// there is an error
if len(serviceLabels) == len(serviceLabelPorts) {
for labelPort := range serviceLabelPorts {
_, err = strconv.Atoi(container.Labels[labelPort])
if err != nil {
break
}
}
} else {
err = errors.New("Port service labels missing, please use traefik.port as default value or define all port service labels")
}
}
return err
}
func (p *Provider) getFrontendName(container dockerData, idx int) string {
// Replace '.' with '-' in quoted keys because of this issue https://github.com/BurntSushi/toml/issues/78
return provider.Normalize(p.getFrontendRule(container))
return provider.Normalize(p.getFrontendRule(container) + "-" + strconv.Itoa(idx))
}
// GetFrontendRule returns the frontend rule for the specified container, using
@ -597,7 +642,7 @@ func (p *Provider) getIPAddress(container dockerData) string {
}
}
if "host" == container.NetworkSettings.NetworkMode {
if container.NetworkSettings.NetworkMode.IsHost() {
if container.Node != nil {
if container.Node.IPAddress != "" {
return container.Node.IPAddress
@ -607,6 +652,21 @@ func (p *Provider) getIPAddress(container dockerData) string {
return "127.0.0.1"
}
if container.NetworkSettings.NetworkMode.IsContainer() {
dockerClient, err := p.createClient()
if err != nil {
log.Warnf("Unable to get IP address for container %s, error: %s", container.Name, err)
return ""
}
ctx := context.Background()
containerInspected, err := dockerClient.ContainerInspect(ctx, container.NetworkSettings.NetworkMode.ConnectedContainer())
if err != nil {
log.Warnf("Unable to get IP address for container %s : Failed to inspect container ID %s, error: %s", container.Name, container.NetworkSettings.NetworkMode.ConnectedContainer(), err)
return ""
}
return p.getIPAddress(parseContainer(containerInspected))
}
if p.UseBindPortIP {
port := p.getPort(container)
for netport, portBindings := range container.NetworkSettings.Ports {

View file

@ -20,38 +20,38 @@ func TestDockerGetFrontendName(t *testing.T) {
}{
{
container: containerJSON(name("foo")),
expected: "Host-foo-docker-localhost",
expected: "Host-foo-docker-localhost-0",
},
{
container: containerJSON(labels(map[string]string{
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
})),
expected: "Headers-User-Agent-bat-0-1-0",
expected: "Headers-User-Agent-bat-0-1-0-0",
},
{
container: containerJSON(labels(map[string]string{
"com.docker.compose.project": "foo",
"com.docker.compose.service": "bar",
})),
expected: "Host-bar-foo-docker-localhost",
expected: "Host-bar-foo-docker-localhost-0",
},
{
container: containerJSON(labels(map[string]string{
types.LabelFrontendRule: "Host:foo.bar",
})),
expected: "Host-foo-bar",
expected: "Host-foo-bar-0",
},
{
container: containerJSON(labels(map[string]string{
types.LabelFrontendRule: "Path:/test",
})),
expected: "Path-test",
expected: "Path-test-0",
},
{
container: containerJSON(labels(map[string]string{
types.LabelFrontendRule: "PathPrefix:/test2",
})),
expected: "PathPrefix-test2",
expected: "PathPrefix-test2-0",
},
}
@ -63,7 +63,7 @@ func TestDockerGetFrontendName(t *testing.T) {
provider := &Provider{
Domain: "docker.localhost",
}
actual := provider.getFrontendName(dockerData)
actual := provider.getFrontendName(dockerData, 0)
if actual != e.expected {
t.Errorf("expected %q, got %q", e.expected, actual)
}
@ -897,13 +897,13 @@ func TestDockerLoadDockerConfig(t *testing.T) {
),
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test-docker-localhost": {
"frontend-Host-test-docker-localhost-0": {
Backend: "backend-test",
PassHostHeader: true,
EntryPoints: []string{},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test-docker-localhost": {
"route-frontend-Host-test-docker-localhost-0": {
Rule: "Host:test.docker.localhost",
},
},
@ -947,24 +947,24 @@ func TestDockerLoadDockerConfig(t *testing.T) {
),
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test1-docker-localhost": {
"frontend-Host-test1-docker-localhost-0": {
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{"http", "https"},
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
Routes: map[string]types.Route{
"route-frontend-Host-test1-docker-localhost": {
"route-frontend-Host-test1-docker-localhost-0": {
Rule: "Host:test1.docker.localhost",
},
},
},
"frontend-Host-test2-docker-localhost": {
"frontend-Host-test2-docker-localhost-1": {
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test2-docker-localhost": {
"route-frontend-Host-test2-docker-localhost-1": {
Rule: "Host:test2.docker.localhost",
},
},
@ -1005,13 +1005,13 @@ func TestDockerLoadDockerConfig(t *testing.T) {
),
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test1-docker-localhost": {
"frontend-Host-test1-docker-localhost-0": {
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{"http", "https"},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test1-docker-localhost": {
"route-frontend-Host-test1-docker-localhost-0": {
Rule: "Host:test1.docker.localhost",
},
},
@ -1104,3 +1104,53 @@ func TestDockerHasStickinessLabel(t *testing.T) {
})
}
}
func TestDockerCheckPortLabels(t *testing.T) {
testCases := []struct {
container docker.ContainerJSON
expectedError bool
}{
{
container: containerJSON(labels(map[string]string{
types.LabelPort: "80",
})),
expectedError: false,
},
{
container: containerJSON(labels(map[string]string{
types.LabelPrefix + "servicename.protocol": "http",
types.LabelPrefix + "servicename.port": "80",
})),
expectedError: false,
},
{
container: containerJSON(labels(map[string]string{
types.LabelPrefix + "servicename.protocol": "http",
types.LabelPort: "80",
})),
expectedError: false,
},
{
container: containerJSON(labels(map[string]string{
types.LabelPrefix + "servicename.protocol": "http",
})),
expectedError: true,
},
}
for containerID, test := range testCases {
test := test
t.Run(strconv.Itoa(containerID), func(t *testing.T) {
t.Parallel()
dockerData := parseContainer(test.container)
err := checkServiceLabelPort(dockerData)
if test.expectedError && err == nil {
t.Error("expected an error but got nil")
} else if !test.expectedError && err != nil {
t.Errorf("expected no error, got %q", err)
}
})
}
}

View file

@ -23,28 +23,28 @@ func TestSwarmGetFrontendName(t *testing.T) {
}{
{
service: swarmService(serviceName("foo")),
expected: "Host-foo-docker-localhost",
expected: "Host-foo-docker-localhost-0",
networks: map[string]*docker.NetworkResource{},
},
{
service: swarmService(serviceLabels(map[string]string{
types.LabelFrontendRule: "Headers:User-Agent,bat/0.1.0",
})),
expected: "Headers-User-Agent-bat-0-1-0",
expected: "Headers-User-Agent-bat-0-1-0-0",
networks: map[string]*docker.NetworkResource{},
},
{
service: swarmService(serviceLabels(map[string]string{
types.LabelFrontendRule: "Host:foo.bar",
})),
expected: "Host-foo-bar",
expected: "Host-foo-bar-0",
networks: map[string]*docker.NetworkResource{},
},
{
service: swarmService(serviceLabels(map[string]string{
types.LabelFrontendRule: "Path:/test",
})),
expected: "Path-test",
expected: "Path-test-0",
networks: map[string]*docker.NetworkResource{},
},
{
@ -54,7 +54,7 @@ func TestSwarmGetFrontendName(t *testing.T) {
types.LabelFrontendRule: "PathPrefix:/test2",
}),
),
expected: "PathPrefix-test2",
expected: "PathPrefix-test2-0",
networks: map[string]*docker.NetworkResource{},
},
}
@ -68,7 +68,7 @@ func TestSwarmGetFrontendName(t *testing.T) {
Domain: "docker.localhost",
SwarmMode: true,
}
actual := provider.getFrontendName(dockerData)
actual := provider.getFrontendName(dockerData, 0)
if actual != e.expected {
t.Errorf("expected %q, got %q", e.expected, actual)
}
@ -660,13 +660,13 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
),
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test-docker-localhost": {
"frontend-Host-test-docker-localhost-0": {
Backend: "backend-test",
PassHostHeader: true,
EntryPoints: []string{},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test-docker-localhost": {
"route-frontend-Host-test-docker-localhost-0": {
Rule: "Host:test.docker.localhost",
},
},
@ -714,24 +714,24 @@ func TestSwarmLoadDockerConfig(t *testing.T) {
),
},
expectedFrontends: map[string]*types.Frontend{
"frontend-Host-test1-docker-localhost": {
"frontend-Host-test1-docker-localhost-0": {
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{"http", "https"},
BasicAuth: []string{"test:$apr1$H6uskkkW$IgXLP6ewTrSuBkTrqE8wj/", "test2:$apr1$d9hr9HBB$4HxwgUir3HP4EsggP/QNo0"},
Routes: map[string]types.Route{
"route-frontend-Host-test1-docker-localhost": {
"route-frontend-Host-test1-docker-localhost-0": {
Rule: "Host:test1.docker.localhost",
},
},
},
"frontend-Host-test2-docker-localhost": {
"frontend-Host-test2-docker-localhost-1": {
Backend: "backend-foobar",
PassHostHeader: true,
EntryPoints: []string{},
BasicAuth: []string{},
Routes: map[string]types.Route{
"route-frontend-Host-test2-docker-localhost": {
"route-frontend-Host-test2-docker-localhost-1": {
Rule: "Host:test2.docker.localhost",
},
},