From 592a12dca23e2292f93a15980a4f1f51b37aafcf Mon Sep 17 00:00:00 2001 From: Diego de Oliveira Date: Sun, 26 Mar 2017 16:59:08 -0300 Subject: [PATCH 1/5] Fix unsound behavior The IP-Per-Task feature changed the behavior for clients without this configuration (using the task IP instead of task hostname). This patch make the new behavior available just for Mesos installation with IP-Per-Task enabled. It also make it possible to force the use of task's hostname. --- docs/toml.md | 10 +++ provider/marathon/marathon.go | 11 ++- provider/marathon/marathon_test.go | 132 ++++++++++++++++++++++++----- traefik.sample.toml | 10 +++ 4 files changed, 137 insertions(+), 26 deletions(-) diff --git a/docs/toml.md b/docs/toml.md index d34eea065..e3d4ae678 100644 --- a/docs/toml.md +++ b/docs/toml.md @@ -969,6 +969,16 @@ domain = "marathon.localhost" # Default: "10s" # # keepAlive = "10s" + +# By default, a task's IP address (as returned by the Marathon API) is used as +# backend server if an IP-per-task configuration can be found; otherwise, the +# name of the host running the task is used. +# The latter behavior can be enforced by enabling this switch. +# +# Optional +# Default: false +# +# forceTaskHostname: false ``` Labels can be used on containers to override default behaviour: diff --git a/provider/marathon/marathon.go b/provider/marathon/marathon.go index 0cd3c6177..e92e9d577 100644 --- a/provider/marathon/marathon.go +++ b/provider/marathon/marathon.go @@ -42,6 +42,7 @@ type Provider struct { TLS *provider.ClientTLS `description:"Enable Docker TLS support"` DialerTimeout flaeg.Duration `description:"Set a non-default connection timeout for Marathon"` KeepAlive flaeg.Duration `description:"Set a non-default TCP Keep Alive time in seconds"` + ForceTaskHostname bool `description:"Force to use the task IP hostname."` Basic *Basic marathonClient marathon.Marathon } @@ -526,11 +527,15 @@ func (p *Provider) getBackendServer(task marathon.Task, applications []marathon. log.Errorf("Unable to get marathon application from task %s", task.AppID) return "" } - if len(task.IPAddresses) == 0 { + switch { + case application.IPAddressPerTask == nil || p.ForceTaskHostname: + return task.Host + case len(task.IPAddresses) == 0: + log.Errorf("Missing marathon IPAddress from task %s", task.AppID) return "" - } else if len(task.IPAddresses) == 1 { + case len(task.IPAddresses) == 1: return task.IPAddresses[0].IPAddress - } else { + default: ipAddressIdxStr, ok := p.getLabel(application, "traefik.ipAddressIdx") if !ok { log.Errorf("Unable to get marathon IPAddress from task %s", task.AppID) diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index 7fc6bbfa9..0a67f7223 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -1,10 +1,13 @@ package marathon import ( + "encoding/json" "errors" "reflect" "testing" + "fmt" + "github.com/containous/traefik/mocks" "github.com/containous/traefik/testhelpers" "github.com/containous/traefik/types" @@ -108,7 +111,7 @@ func TestMarathonLoadConfig(t *testing.T) { "backend-test": { Servers: map[string]types.Server{ "server-test": { - URL: "http://127.0.0.1:80", + URL: "http://localhost:80", Weight: 0, }, }, @@ -161,7 +164,7 @@ func TestMarathonLoadConfig(t *testing.T) { "backend-testLoadBalancerAndCircuitBreaker.dot": { Servers: map[string]types.Server{ "server-testLoadBalancerAndCircuitBreaker-dot": { - URL: "http://127.0.0.1:80", + URL: "http://localhost:80", Weight: 0, }, }, @@ -219,7 +222,7 @@ func TestMarathonLoadConfig(t *testing.T) { "backend-testMaxConn": { Servers: map[string]types.Server{ "server-testMaxConn": { - URL: "http://127.0.0.1:80", + URL: "http://localhost:80", Weight: 0, }, }, @@ -274,7 +277,7 @@ func TestMarathonLoadConfig(t *testing.T) { "backend-testMaxConnOnlySpecifyAmount": { Servers: map[string]types.Server{ "server-testMaxConnOnlySpecifyAmount": { - URL: "http://127.0.0.1:80", + URL: "http://localhost:80", Weight: 0, }, }, @@ -326,7 +329,7 @@ func TestMarathonLoadConfig(t *testing.T) { "backend-testMaxConnOnlyExtractorFunc": { Servers: map[string]types.Server{ "server-testMaxConnOnlyExtractorFunc": { - URL: "http://127.0.0.1:80", + URL: "http://localhost:80", Weight: 0, }, }, @@ -337,27 +340,37 @@ func TestMarathonLoadConfig(t *testing.T) { } for _, c := range cases { - fakeClient := newFakeClient(c.applicationsError, c.applications, c.tasksError, c.tasks) - provider := &Provider{ - Domain: "docker.localhost", - ExposedByDefault: true, - marathonClient: fakeClient, + appID := "" + if len(c.applications.Apps) > 0 { + appID = c.applications.Apps[0].ID } - actualConfig := provider.loadMarathonConfig() - fakeClient.AssertExpectations(t) - if c.expectedNil { - if actualConfig != nil { - t.Fatalf("Should have been nil, got %v", actualConfig) + t.Run(fmt.Sprintf("Running case: %s", appID), func(t *testing.T) { + fakeClient := newFakeClient(c.applicationsError, c.applications, c.tasksError, c.tasks) + provider := &Provider{ + Domain: "docker.localhost", + ExposedByDefault: true, + marathonClient: fakeClient, } - } else { - // Compare backends - if !reflect.DeepEqual(actualConfig.Backends, c.expectedBackends) { - t.Fatalf("expected %#v, got %#v", c.expectedBackends, actualConfig.Backends) + actualConfig := provider.loadMarathonConfig() + fakeClient.AssertExpectations(t) + if c.expectedNil { + if actualConfig != nil { + t.Fatalf("Should have been nil, got %v", actualConfig) + } + } else { + // Compare backends + if !reflect.DeepEqual(actualConfig.Backends, c.expectedBackends) { + expected, _ := json.Marshal(c.expectedBackends) + actual, _ := json.Marshal(actualConfig.Backends) + t.Fatalf("expected\t %s\n, \tgot %s\n", expected, actual) + } + if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) { + expected, _ := json.Marshal(c.expectedFrontends) + actual, _ := json.Marshal(actualConfig.Frontends) + t.Fatalf("expected\t %s\n, got\t %s\n", expected, actual) + } } - if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) { - t.Fatalf("expected %#v, got %#v", c.expectedFrontends, actualConfig.Frontends) - } - } + }) } } @@ -1451,3 +1464,76 @@ func TestMarathonGetSubDomain(t *testing.T) { } } } + +func TestGetBackendServer(t *testing.T) { + + applications := []struct { + forceTaskHostname bool + application marathon.Application + expected string + }{ + { + application: marathon.Application{ + ID: "app-without-IP-per-task", + }, + expected: "sample.com", + }, + { + application: marathon.Application{ + + ID: "app-with-IP-per-task", + IPAddressPerTask: &marathon.IPAddressPerTask{ + Discovery: &marathon.Discovery{ + Ports: &[]marathon.Port{ + { + Number: 8880, + Name: "p1", + }, + }, + }, + }, + }, + expected: "192.168.0.1", + }, { + forceTaskHostname: true, + application: marathon.Application{ + ID: "app-with-hostname-enforced", + IPAddressPerTask: &marathon.IPAddressPerTask{ + Discovery: &marathon.Discovery{ + Ports: &[]marathon.Port{ + { + Number: 8880, + Name: "p1", + }, + }, + }, + }, + }, + expected: "sample.com", + }, + } + + for _, app := range applications { + t.Run(fmt.Sprintf("running %s", app.application.ID), func(t *testing.T) { + provider := &Provider{} + provider.ForceTaskHostname = app.forceTaskHostname + + applications := []marathon.Application{app.application} + + task := marathon.Task{ + AppID: app.application.ID, + Host: "sample.com", + IPAddresses: []*marathon.IPAddress{ + { + IPAddress: "192.168.0.1", + }, + }, + } + + actual := provider.getBackendServer(task, applications) + if actual != app.expected { + t.Errorf("App %s, expected %q, got %q", task.AppID, app.expected, actual) + } + }) + } +} diff --git a/traefik.sample.toml b/traefik.sample.toml index a2a6a8f5d..dc466dd49 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -599,6 +599,16 @@ # # keepAlive = "10s" +# By default, a task's IP address (as returned by the Marathon API) is used as +# backend server if an IP-per-task configuration can be found; otherwise, the +# name of the host running the task is used. +# The latter behavior can be enforced by enabling this switch. +# +# Optional +# Default: false +# +# forceTaskHostname: false + ################################################################ # Mesos configuration backend ################################################################ From e615e833bcf223b4b78e1b2cc65f999a8ab16bf0 Mon Sep 17 00:00:00 2001 From: Timo Reimann Date: Fri, 21 Apr 2017 16:06:05 +0200 Subject: [PATCH 2/5] Use go-spew to display diffs. --- provider/marathon/marathon_test.go | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index 0a67f7223..8e69a8eca 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -1,7 +1,6 @@ package marathon import ( - "encoding/json" "errors" "reflect" "testing" @@ -11,6 +10,7 @@ import ( "github.com/containous/traefik/mocks" "github.com/containous/traefik/testhelpers" "github.com/containous/traefik/types" + "github.com/davecgh/go-spew/spew" "github.com/gambol99/go-marathon" "github.com/stretchr/testify/mock" ) @@ -360,14 +360,10 @@ func TestMarathonLoadConfig(t *testing.T) { } else { // Compare backends if !reflect.DeepEqual(actualConfig.Backends, c.expectedBackends) { - expected, _ := json.Marshal(c.expectedBackends) - actual, _ := json.Marshal(actualConfig.Backends) - t.Fatalf("expected\t %s\n, \tgot %s\n", expected, actual) + t.Errorf("got %v, want %v", spew.Sdump(actualConfig.Backends), spew.Sdump(c.expectedBackends)) } if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) { - expected, _ := json.Marshal(c.expectedFrontends) - actual, _ := json.Marshal(actualConfig.Frontends) - t.Fatalf("expected\t %s\n, got\t %s\n", expected, actual) + t.Errorf("got %v, want %v", spew.Sdump(actualConfig.Frontends), spew.Sdump(c.expectedFrontends)) } } }) From 16c86022bb8f3d3d71493ca397e879b46af881d8 Mon Sep 17 00:00:00 2001 From: Timo Reimann Date: Fri, 21 Apr 2017 16:06:14 +0200 Subject: [PATCH 3/5] Cosmetic changes. --- provider/marathon/marathon.go | 15 +++++++++------ provider/marathon/marathon_test.go | 11 ++++++----- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/provider/marathon/marathon.go b/provider/marathon/marathon.go index e92e9d577..2751f030d 100644 --- a/provider/marathon/marathon.go +++ b/provider/marathon/marathon.go @@ -42,7 +42,7 @@ type Provider struct { TLS *provider.ClientTLS `description:"Enable Docker TLS support"` DialerTimeout flaeg.Duration `description:"Set a non-default connection timeout for Marathon"` KeepAlive flaeg.Duration `description:"Set a non-default TCP Keep Alive time in seconds"` - ForceTaskHostname bool `description:"Force to use the task IP hostname."` + ForceTaskHostname bool `description:"Force to use the task's hostname."` Basic *Basic marathonClient marathon.Marathon } @@ -527,25 +527,28 @@ func (p *Provider) getBackendServer(task marathon.Task, applications []marathon. log.Errorf("Unable to get marathon application from task %s", task.AppID) return "" } + + numTaskIPAddresses := len(task.IPAddresses) switch { case application.IPAddressPerTask == nil || p.ForceTaskHostname: return task.Host - case len(task.IPAddresses) == 0: - log.Errorf("Missing marathon IPAddress from task %s", task.AppID) + case numTaskIPAddresses == 0: + log.Errorf("Missing IP address for Marathon application %s on task %s", application.ID, task.ID) return "" - case len(task.IPAddresses) == 1: + case numTaskIPAddresses == 1: return task.IPAddresses[0].IPAddress default: ipAddressIdxStr, ok := p.getLabel(application, "traefik.ipAddressIdx") if !ok { - log.Errorf("Unable to get marathon IPAddress from task %s", task.AppID) + log.Errorf("Found %d task IP addresses but missing IP address index for Marathon application %s on task %s", numTaskIPAddresses, application.ID, task.ID) return "" } ipAddressIdx, err := strconv.Atoi(ipAddressIdxStr) if err != nil { - log.Errorf("Invalid marathon IPAddress from task %s", task.AppID) + log.Errorf("Cannot use IP address index to select from %d task IP addresses for Marathon application %s on task %s: %s", numTaskIPAddresses, application.ID, task.ID, err) return "" } + return task.IPAddresses[ipAddressIdx].IPAddress } } diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index 8e69a8eca..ab9b0ba09 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -344,7 +344,8 @@ func TestMarathonLoadConfig(t *testing.T) { if len(c.applications.Apps) > 0 { appID = c.applications.Apps[0].ID } - t.Run(fmt.Sprintf("Running case: %s", appID), func(t *testing.T) { + t.Run(fmt.Sprintf("app ID: %s", appID), func(t *testing.T) { + t.Parallel() fakeClient := newFakeClient(c.applicationsError, c.applications, c.tasksError, c.tasks) provider := &Provider{ Domain: "docker.localhost", @@ -355,15 +356,15 @@ func TestMarathonLoadConfig(t *testing.T) { fakeClient.AssertExpectations(t) if c.expectedNil { if actualConfig != nil { - t.Fatalf("Should have been nil, got %v", actualConfig) + t.Fatalf("configuration should have been nil, got %v", actualConfig) } } else { // Compare backends if !reflect.DeepEqual(actualConfig.Backends, c.expectedBackends) { - t.Errorf("got %v, want %v", spew.Sdump(actualConfig.Backends), spew.Sdump(c.expectedBackends)) + t.Errorf("got backend %v, want %v", spew.Sdump(actualConfig.Backends), spew.Sdump(c.expectedBackends)) } if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) { - t.Errorf("got %v, want %v", spew.Sdump(actualConfig.Frontends), spew.Sdump(c.expectedFrontends)) + t.Errorf("got frontend %v, want %v", spew.Sdump(actualConfig.Frontends), spew.Sdump(c.expectedFrontends)) } } }) @@ -1510,7 +1511,7 @@ func TestGetBackendServer(t *testing.T) { } for _, app := range applications { - t.Run(fmt.Sprintf("running %s", app.application.ID), func(t *testing.T) { + t.Run(app.application.ID, func(t *testing.T) { provider := &Provider{} provider.ForceTaskHostname = app.forceTaskHostname From a4355569af800315d706b6876340f76c1c8fe07d Mon Sep 17 00:00:00 2001 From: Timo Reimann Date: Sat, 22 Apr 2017 22:07:27 +0200 Subject: [PATCH 4/5] Extract index functionality into generic helper function. Allows to move specific test cases to dedicated tests for new function. --- provider/marathon/marathon.go | 24 +++++--- provider/marathon/marathon_test.go | 90 +++++++++++++++++++----------- 2 files changed, 75 insertions(+), 39 deletions(-) diff --git a/provider/marathon/marathon.go b/provider/marathon/marathon.go index 2751f030d..643cacccb 100644 --- a/provider/marathon/marathon.go +++ b/provider/marathon/marathon.go @@ -482,12 +482,9 @@ func processPorts(application marathon.Application, task marathon.Task) (int, er portIndexLabel, ok := (*application.Labels)[labelPortIndex] if ok { var err error - portIndex, err = strconv.Atoi(portIndexLabel) - switch { - case err != nil: - return 0, fmt.Errorf("failed to parse port index label: %s", err) - case portIndex < 0, portIndex > len(ports)-1: - return 0, fmt.Errorf("port index %d must be within port range (0, %d)", portIndex, len(ports)-1) + portIndex, err = parseIndex(portIndexLabel, len(ports)) + if err != nil { + return 0, fmt.Errorf("cannot use port index to select from %d ports: %s", len(ports), err) } } return ports[portIndex], nil @@ -543,7 +540,8 @@ func (p *Provider) getBackendServer(task marathon.Task, applications []marathon. log.Errorf("Found %d task IP addresses but missing IP address index for Marathon application %s on task %s", numTaskIPAddresses, application.ID, task.ID) return "" } - ipAddressIdx, err := strconv.Atoi(ipAddressIdxStr) + + ipAddressIdx, err := parseIndex(ipAddressIdxStr, numTaskIPAddresses) if err != nil { log.Errorf("Cannot use IP address index to select from %d task IP addresses for Marathon application %s on task %s: %s", numTaskIPAddresses, application.ID, task.ID, err) return "" @@ -552,3 +550,15 @@ func (p *Provider) getBackendServer(task marathon.Task, applications []marathon. return task.IPAddresses[ipAddressIdx].IPAddress } } + +func parseIndex(index string, length int) (int, error) { + parsed, err := strconv.Atoi(index) + switch { + case err != nil: + return 0, fmt.Errorf("failed to parse index '%s': %s", index, err) + case parsed < 0, parsed > length-1: + return 0, fmt.Errorf("index %d must be within range (0, %d)", parsed, length-1) + } + + return parsed, nil +} diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index ab9b0ba09..cef215510 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -1067,38 +1067,6 @@ func TestMarathonGetPort(t *testing.T) { }, expected: "", }, - { - desc: "sub-zero port index specified", - applications: []marathon.Application{ - { - ID: "app", - Labels: &map[string]string{ - "traefik.portIndex": "-1", - }, - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80}, - }, - expected: "", - }, - { - desc: "too high port index specified", - applications: []marathon.Application{ - { - ID: "app", - Labels: &map[string]string{ - "traefik.portIndex": "42", - }, - }, - }, - task: marathon.Task{ - AppID: "app", - Ports: []int{80, 443}, - }, - expected: "", - }, { desc: "task port preferred over application port", applications: []marathon.Application{ @@ -1534,3 +1502,61 @@ func TestGetBackendServer(t *testing.T) { }) } } + +func TestParseIndex(t *testing.T) { + tests := []struct { + idxStr string + length int + shouldSucceed bool + parsed int + }{ + { + idxStr: "illegal", + length: 42, + shouldSucceed: false, + }, + { + idxStr: "-1", + length: 42, + shouldSucceed: false, + }, + { + idxStr: "10", + length: 1, + shouldSucceed: false, + }, + { + idxStr: "10", + length: 10, + shouldSucceed: false, + }, + { + idxStr: "0", + length: 1, + shouldSucceed: true, + parsed: 0, + }, + { + idxStr: "10", + length: 11, + shouldSucceed: true, + parsed: 10, + }, + } + + for _, test := range tests { + test := test + t.Run(fmt.Sprintf("parseIndex(%s, %d)", test.idxStr, test.length), func(t *testing.T) { + t.Parallel() + parsed, err := parseIndex(test.idxStr, test.length) + + if test.shouldSucceed != (err == nil) { + t.Fatalf("got error '%s', want error: %t", err, !test.shouldSucceed) + } + + if test.shouldSucceed && parsed != test.parsed { + t.Errorf("got parsed index %d, want %d", parsed, test.parsed) + } + }) + } +} From 7eb3051a57d383df972a660667b507bcb86de977 Mon Sep 17 00:00:00 2001 From: Timo Reimann Date: Sat, 22 Apr 2017 22:51:22 +0200 Subject: [PATCH 5/5] Improve and extend TestGetBackendServer. - Cover error cases. - Use sub-tests. --- provider/marathon/marathon_test.go | 170 ++++++++++++++++++++--------- 1 file changed, 118 insertions(+), 52 deletions(-) diff --git a/provider/marathon/marathon_test.go b/provider/marathon/marathon_test.go index cef215510..60144f0c6 100644 --- a/provider/marathon/marathon_test.go +++ b/provider/marathon/marathon_test.go @@ -1431,73 +1431,139 @@ func TestMarathonGetSubDomain(t *testing.T) { } func TestGetBackendServer(t *testing.T) { - - applications := []struct { - forceTaskHostname bool + appID := "appId" + host := "host" + tests := []struct { + desc string application marathon.Application - expected string + addIPAddrPerTask bool + task marathon.Task + forceTaskHostname bool + wantServer string }{ { - application: marathon.Application{ - ID: "app-without-IP-per-task", - }, - expected: "sample.com", + desc: "application missing", + application: marathon.Application{ID: "other"}, + wantServer: "", }, { - application: marathon.Application{ - - ID: "app-with-IP-per-task", - IPAddressPerTask: &marathon.IPAddressPerTask{ - Discovery: &marathon.Discovery{ - Ports: &[]marathon.Port{ - { - Number: 8880, - Name: "p1", - }, - }, - }, - }, - }, - expected: "192.168.0.1", - }, { + desc: "application without IP-per-task", + wantServer: host, + }, + { + desc: "task hostname override", + addIPAddrPerTask: true, forceTaskHostname: true, - application: marathon.Application{ - ID: "app-with-hostname-enforced", - IPAddressPerTask: &marathon.IPAddressPerTask{ - Discovery: &marathon.Discovery{ - Ports: &[]marathon.Port{ - { - Number: 8880, - Name: "p1", - }, - }, + wantServer: host, + }, + { + desc: "task IP address missing", + task: marathon.Task{ + IPAddresses: []*marathon.IPAddress{}, + }, + addIPAddrPerTask: true, + wantServer: "", + }, + { + desc: "single task IP address", + task: marathon.Task{ + IPAddresses: []*marathon.IPAddress{ + { + IPAddress: "1.1.1.1", }, }, }, - expected: "sample.com", + addIPAddrPerTask: true, + wantServer: "1.1.1.1", + }, + { + desc: "multiple task IP addresses without index label", + task: marathon.Task{ + IPAddresses: []*marathon.IPAddress{ + { + IPAddress: "1.1.1.1", + }, + { + IPAddress: "2.2.2.2", + }, + }, + }, + addIPAddrPerTask: true, + wantServer: "", + }, + { + desc: "multiple task IP addresses with invalid index label", + application: marathon.Application{ + Labels: &map[string]string{"traefik.ipAddressIdx": "invalid"}, + }, + task: marathon.Task{ + IPAddresses: []*marathon.IPAddress{ + { + IPAddress: "1.1.1.1", + }, + { + IPAddress: "2.2.2.2", + }, + }, + }, + addIPAddrPerTask: true, + wantServer: "", + }, + { + desc: "multiple task IP addresses with valid index label", + application: marathon.Application{ + Labels: &map[string]string{"traefik.ipAddressIdx": "1"}, + }, + task: marathon.Task{ + IPAddresses: []*marathon.IPAddress{ + { + IPAddress: "1.1.1.1", + }, + { + IPAddress: "2.2.2.2", + }, + }, + }, + addIPAddrPerTask: true, + wantServer: "2.2.2.2", }, } - for _, app := range applications { - t.Run(app.application.ID, func(t *testing.T) { - provider := &Provider{} - provider.ForceTaskHostname = app.forceTaskHostname + for _, test := range tests { + test := test + t.Run(test.desc, func(t *testing.T) { + t.Parallel() + provider := &Provider{ForceTaskHostname: test.forceTaskHostname} - applications := []marathon.Application{app.application} - - task := marathon.Task{ - AppID: app.application.ID, - Host: "sample.com", - IPAddresses: []*marathon.IPAddress{ - { - IPAddress: "192.168.0.1", - }, - }, + // Populate application. + if test.application.ID == "" { + test.application.ID = appID } + if test.application.Labels == nil { + test.application.Labels = &map[string]string{} + } + if test.addIPAddrPerTask { + test.application.IPAddressPerTask = &marathon.IPAddressPerTask{ + Discovery: &marathon.Discovery{ + Ports: &[]marathon.Port{ + { + Number: 8000, + Name: "port", + }, + }, + }, + } + } + applications := []marathon.Application{test.application} - actual := provider.getBackendServer(task, applications) - if actual != app.expected { - t.Errorf("App %s, expected %q, got %q", task.AppID, app.expected, actual) + // Populate task. + test.task.AppID = appID + test.task.Host = "host" + + gotServer := provider.getBackendServer(test.task, applications) + + if gotServer != test.wantServer { + t.Errorf("got server '%s', want '%s'", gotServer, test.wantServer) } }) }