refactor(mesos) be testable.
This commit is contained in:
parent
e9d2124885
commit
17137ba3e7
4 changed files with 466 additions and 302 deletions
|
@ -6,56 +6,48 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/ty/fun"
|
||||
"github.com/containous/traefik/log"
|
||||
"github.com/containous/traefik/provider"
|
||||
"github.com/containous/traefik/provider/label"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/mesosphere/mesos-dns/records"
|
||||
"github.com/mesosphere/mesos-dns/records/state"
|
||||
)
|
||||
|
||||
func (p *Provider) buildConfiguration() *types.Configuration {
|
||||
func (p *Provider) buildConfiguration(tasks []state.Task) *types.Configuration {
|
||||
var mesosFuncMap = template.FuncMap{
|
||||
"getBackend": getBackend,
|
||||
"getPort": p.getPort,
|
||||
"getHost": p.getHost,
|
||||
"getWeight": getFuncApplicationStringValue(label.TraefikWeight, label.DefaultWeight),
|
||||
"getDomain": getFuncStringValue(label.TraefikDomain, p.Domain),
|
||||
"getProtocol": getFuncApplicationStringValue(label.TraefikProtocol, label.DefaultProtocol),
|
||||
"getDomain": getFuncStringValue(label.TraefikDomain, p.Domain),
|
||||
"getID": getID,
|
||||
|
||||
// Backend functions
|
||||
"getProtocol": getFuncApplicationStringValue(label.TraefikProtocol, label.DefaultProtocol),
|
||||
"getPort": p.getPort,
|
||||
"getHost": p.getHost,
|
||||
"getWeight": getFuncApplicationStringValue(label.TraefikWeight, label.DefaultWeight),
|
||||
"getBackend": getBackend,
|
||||
|
||||
// Frontend functions
|
||||
"getPassHostHeader": getFuncStringValue(label.TraefikFrontendPassHostHeader, label.DefaultPassHostHeader),
|
||||
"getPriority": getFuncStringValue(label.TraefikFrontendPriority, label.DefaultFrontendPriority),
|
||||
"getEntryPoints": getFuncSliceStringValue(label.TraefikFrontendEntryPoints),
|
||||
"getFrontendRule": p.getFrontendRule,
|
||||
"getFrontendBackend": getFrontendBackend,
|
||||
"getID": getID,
|
||||
"getFrontEndName": getFrontEndName,
|
||||
}
|
||||
|
||||
rg := records.NewRecordGenerator(time.Duration(p.StateTimeoutSecond) * time.Second)
|
||||
st, err := rg.FindMaster(p.Masters...)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create a client for Mesos, error: %v", err)
|
||||
return nil
|
||||
}
|
||||
tasks := taskRecords(st)
|
||||
|
||||
// filter tasks
|
||||
filteredTasks := fun.Filter(func(task state.Task) bool {
|
||||
return taskFilter(task, p.ExposedByDefault)
|
||||
}, tasks).([]state.Task)
|
||||
|
||||
uniqueApps := make(map[string]state.Task)
|
||||
for _, value := range filteredTasks {
|
||||
if _, ok := uniqueApps[value.DiscoveryInfo.Name]; !ok {
|
||||
uniqueApps[value.DiscoveryInfo.Name] = value
|
||||
}
|
||||
}
|
||||
var filteredApps []state.Task
|
||||
for _, value := range uniqueApps {
|
||||
filteredApps = append(filteredApps, value)
|
||||
uniqueApps := make(map[string]struct{})
|
||||
for _, task := range filteredTasks {
|
||||
if _, ok := uniqueApps[task.DiscoveryInfo.Name]; !ok {
|
||||
uniqueApps[task.DiscoveryInfo.Name] = struct{}{}
|
||||
filteredApps = append(filteredApps, task)
|
||||
}
|
||||
}
|
||||
|
||||
templateObjects := struct {
|
||||
|
@ -75,31 +67,12 @@ func (p *Provider) buildConfiguration() *types.Configuration {
|
|||
return configuration
|
||||
}
|
||||
|
||||
func taskRecords(st state.State) []state.Task {
|
||||
var tasks []state.Task
|
||||
for _, f := range st.Frameworks {
|
||||
for _, task := range f.Tasks {
|
||||
for _, slave := range st.Slaves {
|
||||
if task.SlaveID == slave.ID {
|
||||
task.SlaveIP = slave.PID.Host
|
||||
}
|
||||
}
|
||||
|
||||
// only do running and discoverable tasks
|
||||
if task.State == "TASK_RUNNING" {
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
||||
func taskFilter(task state.Task, exposedByDefaultFlag bool) bool {
|
||||
if len(task.DiscoveryInfo.Ports.DiscoveryPorts) == 0 {
|
||||
log.Debugf("Filtering Mesos task without port %s", task.Name)
|
||||
return false
|
||||
}
|
||||
|
||||
if !isEnabled(task, exposedByDefaultFlag) {
|
||||
log.Debugf("Filtering disabled Mesos task %s", task.DiscoveryInfo.Name)
|
||||
return false
|
||||
|
@ -140,7 +113,7 @@ func taskFilter(task state.Task, exposedByDefaultFlag bool) bool {
|
|||
}
|
||||
}
|
||||
|
||||
//filter healthChecks
|
||||
// filter healthChecks
|
||||
if task.Statuses != nil && len(task.Statuses) > 0 && task.Statuses[0].Healthy != nil && !*task.Statuses[0].Healthy {
|
||||
log.Debugf("Filtering Mesos task %s with bad healthCheck", task.DiscoveryInfo.Name)
|
||||
return false
|
||||
|
@ -166,7 +139,7 @@ func getFrontendBackend(task state.Task) string {
|
|||
if value := getStringValue(task, label.TraefikBackend, ""); len(value) > 0 {
|
||||
return value
|
||||
}
|
||||
return "-" + provider.Normalize(task.DiscoveryInfo.Name)
|
||||
return provider.Normalize(task.DiscoveryInfo.Name)
|
||||
}
|
||||
|
||||
func getFrontEndName(task state.Task) string {
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
package mesos
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"testing"
|
||||
|
||||
"github.com/containous/traefik/provider/label"
|
||||
|
@ -10,238 +8,362 @@ import (
|
|||
"github.com/mesos/mesos-go/upid"
|
||||
"github.com/mesosphere/mesos-dns/records/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// FIXME fill this test!!
|
||||
func TestBuildConfiguration(t *testing.T) {
|
||||
cases := []struct {
|
||||
applicationsError bool
|
||||
tasksError bool
|
||||
mesosTask state.Task
|
||||
expected bool
|
||||
exposedByDefault bool
|
||||
expectedNil bool
|
||||
p := &Provider{
|
||||
Domain: "docker.localhost",
|
||||
ExposedByDefault: true,
|
||||
IPSources: "host",
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
tasks []state.Task
|
||||
expectedFrontends map[string]*types.Frontend
|
||||
expectedBackends map[string]*types.Backend
|
||||
}{}
|
||||
}{
|
||||
{
|
||||
desc: "should return an empty configuration when no task",
|
||||
tasks: []state.Task{},
|
||||
expectedFrontends: map[string]*types.Frontend{},
|
||||
expectedBackends: map[string]*types.Backend{},
|
||||
},
|
||||
{
|
||||
desc: "2 applications with 2 tasks",
|
||||
tasks: []state.Task{
|
||||
// App 1
|
||||
aTask("ID1",
|
||||
withIP("10.10.10.10"),
|
||||
withInfo("name1",
|
||||
withPorts(withPort("TCP", 80, "WEB"))),
|
||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
||||
),
|
||||
aTask("ID2",
|
||||
withIP("10.10.10.11"),
|
||||
withInfo("name1",
|
||||
withPorts(withPort("TCP", 81, "WEB"))),
|
||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
||||
),
|
||||
// App 2
|
||||
aTask("ID3",
|
||||
withIP("20.10.10.10"),
|
||||
withInfo("name2",
|
||||
withPorts(withPort("TCP", 80, "WEB"))),
|
||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
||||
),
|
||||
aTask("ID4",
|
||||
withIP("20.10.10.11"),
|
||||
withInfo("name2",
|
||||
withPorts(withPort("TCP", 81, "WEB"))),
|
||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-ID1": {
|
||||
Backend: "backend-name1",
|
||||
EntryPoints: []string{},
|
||||
PassHostHeader: true,
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-ID1": {
|
||||
Rule: "Host:name1.docker.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
"frontend-ID3": {
|
||||
Backend: "backend-name2",
|
||||
EntryPoints: []string{},
|
||||
PassHostHeader: true,
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-ID3": {
|
||||
Rule: "Host:name2.docker.localhost",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-name1": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-ID1": {
|
||||
URL: "http://10.10.10.10:80",
|
||||
Weight: 0,
|
||||
},
|
||||
"server-ID2": {
|
||||
URL: "http://10.10.10.11:81",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
"backend-name2": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-ID3": {
|
||||
URL: "http://20.10.10.10:80",
|
||||
Weight: 0,
|
||||
},
|
||||
"server-ID4": {
|
||||
URL: "http://20.10.10.11:81",
|
||||
Weight: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "with all labels",
|
||||
tasks: []state.Task{
|
||||
aTask("ID1",
|
||||
withLabel(label.TraefikPort, "666"),
|
||||
withLabel(label.TraefikProtocol, "https"),
|
||||
withLabel(label.TraefikWeight, "12"),
|
||||
|
||||
for _, c := range cases {
|
||||
provider := &Provider{
|
||||
Domain: "docker.localhost",
|
||||
ExposedByDefault: true,
|
||||
}
|
||||
actualConfig := provider.buildConfiguration()
|
||||
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) {
|
||||
t.Fatalf("Expected %#v, got %#v", c.expectedBackends, actualConfig.Backends)
|
||||
}
|
||||
if !reflect.DeepEqual(actualConfig.Frontends, c.expectedFrontends) {
|
||||
t.Fatalf("Expected %#v, got %#v", c.expectedFrontends, actualConfig.Frontends)
|
||||
}
|
||||
}
|
||||
withLabel(label.TraefikBackend, "foobar"),
|
||||
|
||||
withLabel(label.TraefikFrontendEntryPoints, "http,https"),
|
||||
withLabel(label.TraefikFrontendPassHostHeader, "true"),
|
||||
withLabel(label.TraefikFrontendPassTLSCert, "true"),
|
||||
withLabel(label.TraefikFrontendPriority, "666"),
|
||||
withLabel(label.TraefikFrontendRule, "Host:traefik.io"),
|
||||
|
||||
withIP("10.10.10.10"),
|
||||
withInfo("name1", withPorts(
|
||||
withPortTCP(80, "n"),
|
||||
withPortTCP(666, "n"))),
|
||||
withStatus(withHealthy(true), withState("TASK_RUNNING")),
|
||||
),
|
||||
},
|
||||
expectedFrontends: map[string]*types.Frontend{
|
||||
"frontend-ID1": {
|
||||
EntryPoints: []string{
|
||||
"http",
|
||||
"https",
|
||||
},
|
||||
Backend: "backend-foobar",
|
||||
Routes: map[string]types.Route{
|
||||
"route-host-ID1": {
|
||||
Rule: "Host:traefik.io",
|
||||
},
|
||||
},
|
||||
PassHostHeader: true,
|
||||
Priority: 666,
|
||||
},
|
||||
},
|
||||
expectedBackends: map[string]*types.Backend{
|
||||
"backend-foobar": {
|
||||
Servers: map[string]types.Server{
|
||||
"server-ID1": {
|
||||
URL: "https://10.10.10.10:666",
|
||||
Weight: 12,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
|
||||
actualConfig := p.buildConfiguration(test.tasks)
|
||||
|
||||
require.NotNil(t, actualConfig)
|
||||
assert.Equal(t, test.expectedBackends, actualConfig.Backends)
|
||||
assert.Equal(t, test.expectedFrontends, actualConfig.Frontends)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTaskFilter(t *testing.T) {
|
||||
testCases := []struct {
|
||||
desc string
|
||||
mesosTask state.Task
|
||||
expected bool
|
||||
exposedByDefault bool
|
||||
expected bool
|
||||
}{
|
||||
{
|
||||
desc: "no task",
|
||||
mesosTask: state.Task{},
|
||||
exposedByDefault: true,
|
||||
expected: false,
|
||||
exposedByDefault: true,
|
||||
},
|
||||
{
|
||||
mesosTask: task(statuses(status(setState("TASK_RUNNING")))),
|
||||
desc: "task not healthy",
|
||||
mesosTask: aTask("test", withStatus(withState("TASK_RUNNING"))),
|
||||
exposedByDefault: true,
|
||||
expected: false,
|
||||
exposedByDefault: true,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "false"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
desc: "exposedByDefault false and traefik.enable false",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "false"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: false, // because label traefik.enable = false
|
||||
exposedByDefault: false,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
desc: "traefik.enable = true",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: true,
|
||||
exposedByDefault: false,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
),
|
||||
expected: true,
|
||||
exposedByDefault: true,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "false"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
desc: "exposedByDefault true and traefik.enable true",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: false, // because label traefik.enable = false (even wherek exposedByDefault = true)
|
||||
exposedByDefault: true,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPortIndex, "1",
|
||||
label.TraefikPort, "80"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
),
|
||||
expected: false, // traefik.portIndex & traefik.port cannot be set both
|
||||
exposedByDefault: true,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPortIndex, "1"),
|
||||
discovery(setDiscoveryPorts("TCP", 80, "WEB HTTP", "TCP", 443, "WEB HTTPS")),
|
||||
),
|
||||
expected: true,
|
||||
exposedByDefault: true,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true"),
|
||||
discovery(setDiscoveryPorts("TCP", 80, "WEB HTTP", "TCP", 443, "WEB HTTPS")),
|
||||
desc: "exposedByDefault true and traefik.enable false",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "false"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: true, // Default to first index
|
||||
exposedByDefault: true,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPortIndex, "1"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
desc: "traefik.portIndex and traefik.port both set",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPortIndex, "1"),
|
||||
withLabel(label.TraefikEnable, "80"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: false, // traefik.portIndex and discoveryPorts don't correspond
|
||||
exposedByDefault: true,
|
||||
}, {
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPortIndex, "0"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "valid traefik.portIndex",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPortIndex, "1"),
|
||||
withInfo("test", withPorts(
|
||||
withPortTCP(80, "WEB"),
|
||||
withPortTCP(443, "WEB HTTPS"),
|
||||
)),
|
||||
),
|
||||
expected: true, // traefik.portIndex and discoveryPorts correspond
|
||||
exposedByDefault: true,
|
||||
}, {
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPort, "TRAEFIK"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "default to first port index",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withInfo("test", withPorts(
|
||||
withPortTCP(80, "WEB"),
|
||||
withPortTCP(443, "WEB HTTPS"),
|
||||
)),
|
||||
),
|
||||
expected: false, // traefik.port is not an integer
|
||||
exposedByDefault: true,
|
||||
}, {
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPort, "443"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "traefik.portIndex and discoveryPorts don't correspond",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPortIndex, "1"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: false, // traefik.port is not the same as discovery.port
|
||||
exposedByDefault: true,
|
||||
}, {
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(true))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPort, "80"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "traefik.portIndex and discoveryPorts correspond",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPortIndex, "0"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: true, // traefik.port is the same as discovery.port
|
||||
exposedByDefault: true,
|
||||
}, {
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPort, "80"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "traefik.port is not an integer",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPort, "TRAEFIK"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: true, // No healthCheck
|
||||
exposedByDefault: true,
|
||||
}, {
|
||||
mesosTask: task(
|
||||
statuses(
|
||||
status(
|
||||
setState("TASK_RUNNING"),
|
||||
setHealthy(false))),
|
||||
setLabels(label.TraefikEnable, "true",
|
||||
label.TraefikPort, "80"),
|
||||
discovery(setDiscoveryPort("TCP", 80, "WEB")),
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "traefik.port is not the same as discovery.port",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPort, "443"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
expected: false, // HealthCheck at false
|
||||
exposedByDefault: true,
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
desc: "traefik.port is the same as discovery.port",
|
||||
mesosTask: aTask("test",
|
||||
withDefaultStatus(),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPort, "80"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
exposedByDefault: true,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "healthy nil",
|
||||
mesosTask: aTask("test",
|
||||
withStatus(
|
||||
withState("TASK_RUNNING"),
|
||||
),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPort, "80"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
exposedByDefault: true,
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
desc: "healthy false",
|
||||
mesosTask: aTask("test",
|
||||
withStatus(
|
||||
withState("TASK_RUNNING"),
|
||||
withHealthy(false),
|
||||
),
|
||||
withLabel(label.TraefikEnable, "true"),
|
||||
withLabel(label.TraefikPort, "80"),
|
||||
withInfo("test", withPorts(withPortTCP(80, "WEB"))),
|
||||
),
|
||||
exposedByDefault: true,
|
||||
expected: false,
|
||||
},
|
||||
}
|
||||
|
||||
for index, test := range testCases {
|
||||
t.Run(strconv.Itoa(index), func(t *testing.T) {
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := taskFilter(test.mesosTask, test.exposedByDefault)
|
||||
if actual != test.expected {
|
||||
ok := assert.Equal(t, test.expected, actual)
|
||||
if !ok {
|
||||
t.Logf("Statuses : %v", test.mesosTask.Statuses)
|
||||
t.Logf("Label : %v", test.mesosTask.Labels)
|
||||
t.Logf("DiscoveryInfo : %v", test.mesosTask.DiscoveryInfo)
|
||||
|
@ -303,7 +425,7 @@ func TestGetSubDomain(t *testing.T) {
|
|||
|
||||
for _, test := range testCases {
|
||||
test := test
|
||||
t.Run("", func(t *testing.T) {
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
actual := test.provider.getSubDomain(test.path)
|
||||
|
|
|
@ -12,6 +12,9 @@ import (
|
|||
"github.com/containous/traefik/safe"
|
||||
"github.com/containous/traefik/types"
|
||||
"github.com/mesos/mesos-go/detector"
|
||||
"github.com/mesosphere/mesos-dns/records"
|
||||
"github.com/mesosphere/mesos-dns/records/state"
|
||||
|
||||
// Register mesos zoo the detector
|
||||
_ "github.com/mesos/mesos-go/detector/zoo"
|
||||
"github.com/mesosphere/mesos-dns/detect"
|
||||
|
@ -76,7 +79,8 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||
for {
|
||||
select {
|
||||
case <-reload.C:
|
||||
configuration := p.buildConfiguration()
|
||||
tasks := p.getTasks()
|
||||
configuration := p.buildConfiguration(tasks)
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "mesos",
|
||||
|
@ -92,7 +96,8 @@ func (p *Provider) Provide(configurationChan chan<- types.ConfigMessage, pool *s
|
|||
}
|
||||
log.Debugf("new masters detected: %v", masters)
|
||||
p.Masters = masters
|
||||
configuration := p.buildConfiguration()
|
||||
tasks := p.getTasks()
|
||||
configuration := p.buildConfiguration(tasks)
|
||||
if configuration != nil {
|
||||
configurationChan <- types.ConfigMessage{
|
||||
ProviderName: "mesos",
|
||||
|
@ -129,3 +134,35 @@ func detectMasters(zk string, masters []string) <-chan []string {
|
|||
}
|
||||
return changed
|
||||
}
|
||||
|
||||
func (p *Provider) getTasks() []state.Task {
|
||||
rg := records.NewRecordGenerator(time.Duration(p.StateTimeoutSecond) * time.Second)
|
||||
|
||||
st, err := rg.FindMaster(p.Masters...)
|
||||
if err != nil {
|
||||
log.Errorf("Failed to create a client for Mesos, error: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return taskRecords(st)
|
||||
}
|
||||
|
||||
func taskRecords(st state.State) []state.Task {
|
||||
var tasks []state.Task
|
||||
for _, f := range st.Frameworks {
|
||||
for _, task := range f.Tasks {
|
||||
for _, slave := range st.Slaves {
|
||||
if task.SlaveID == slave.ID {
|
||||
task.SlaveIP = slave.PID.Host
|
||||
}
|
||||
}
|
||||
|
||||
// only do running and discoverable tasks
|
||||
if task.State == "TASK_RUNNING" {
|
||||
tasks = append(tasks, task)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tasks
|
||||
}
|
||||
|
|
|
@ -1,113 +1,145 @@
|
|||
package mesos
|
||||
|
||||
import (
|
||||
"github.com/containous/traefik/log"
|
||||
"testing"
|
||||
|
||||
"github.com/mesosphere/mesos-dns/records/state"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// test helpers
|
||||
|
||||
type (
|
||||
taskOpt func(*state.Task)
|
||||
statusOpt func(*state.Status)
|
||||
)
|
||||
func TestBuilder(t *testing.T) {
|
||||
result := aTask("ID1",
|
||||
withIP("10.10.10.10"),
|
||||
withLabel("foo", "bar"),
|
||||
withLabel("fii", "bar"),
|
||||
withLabel("fuu", "bar"),
|
||||
withInfo("name1",
|
||||
withPorts(withPort("TCP", 80, "p"),
|
||||
withPortTCP(81, "n"))),
|
||||
withStatus(withHealthy(true), withState("a")))
|
||||
|
||||
func task(opts ...taskOpt) state.Task {
|
||||
var t state.Task
|
||||
for _, opt := range opts {
|
||||
opt(&t)
|
||||
}
|
||||
return t
|
||||
expected := state.Task{
|
||||
FrameworkID: "",
|
||||
ID: "ID1",
|
||||
SlaveIP: "10.10.10.10",
|
||||
Name: "",
|
||||
SlaveID: "",
|
||||
State: "",
|
||||
Statuses: []state.Status{{
|
||||
State: "a",
|
||||
Healthy: Bool(true),
|
||||
ContainerStatus: state.ContainerStatus{},
|
||||
}},
|
||||
DiscoveryInfo: state.DiscoveryInfo{
|
||||
Name: "name1",
|
||||
Labels: struct {
|
||||
Labels []state.Label "json:\"labels\""
|
||||
}{},
|
||||
Ports: state.Ports{DiscoveryPorts: []state.DiscoveryPort{
|
||||
{Protocol: "TCP", Number: 80, Name: "p"},
|
||||
{Protocol: "TCP", Number: 81, Name: "n"}}}},
|
||||
Labels: []state.Label{
|
||||
{Key: "foo", Value: "bar"},
|
||||
{Key: "fii", Value: "bar"},
|
||||
{Key: "fuu", Value: "bar"}}}
|
||||
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
|
||||
func statuses(st ...state.Status) taskOpt {
|
||||
return func(t *state.Task) {
|
||||
t.Statuses = append(t.Statuses, st...)
|
||||
func aTask(id string, ops ...func(*state.Task)) state.Task {
|
||||
ts := &state.Task{ID: id}
|
||||
for _, op := range ops {
|
||||
op(ts)
|
||||
}
|
||||
return *ts
|
||||
}
|
||||
|
||||
func withIP(ip string) func(*state.Task) {
|
||||
return func(task *state.Task) {
|
||||
task.SlaveIP = ip
|
||||
}
|
||||
}
|
||||
|
||||
func discovery(dp state.DiscoveryInfo) taskOpt {
|
||||
return func(t *state.Task) {
|
||||
t.DiscoveryInfo = dp
|
||||
func withInfo(name string, ops ...func(*state.DiscoveryInfo)) func(*state.Task) {
|
||||
return func(task *state.Task) {
|
||||
info := &state.DiscoveryInfo{Name: name}
|
||||
for _, op := range ops {
|
||||
op(info)
|
||||
}
|
||||
task.DiscoveryInfo = *info
|
||||
}
|
||||
}
|
||||
|
||||
func setLabels(kvs ...string) taskOpt {
|
||||
return func(t *state.Task) {
|
||||
if len(kvs)%2 != 0 {
|
||||
panic("odd number")
|
||||
func withPorts(ops ...func(port *state.DiscoveryPort)) func(*state.DiscoveryInfo) {
|
||||
return func(info *state.DiscoveryInfo) {
|
||||
var ports []state.DiscoveryPort
|
||||
for _, op := range ops {
|
||||
pt := &state.DiscoveryPort{}
|
||||
op(pt)
|
||||
ports = append(ports, *pt)
|
||||
}
|
||||
|
||||
for i := 0; i < len(kvs); i += 2 {
|
||||
var label = state.Label{Key: kvs[i], Value: kvs[i+1]}
|
||||
log.Debugf("Label1.1 : %v", label)
|
||||
t.Labels = append(t.Labels, label)
|
||||
log.Debugf("Label1.2 : %v", t.Labels)
|
||||
info.Ports = state.Ports{
|
||||
DiscoveryPorts: ports,
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
func status(opts ...statusOpt) state.Status {
|
||||
var s state.Status
|
||||
for _, opt := range opts {
|
||||
opt(&s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func setDiscoveryPort(proto string, port int, name string) state.DiscoveryInfo {
|
||||
|
||||
dp := state.DiscoveryPort{
|
||||
Protocol: proto,
|
||||
Number: port,
|
||||
Name: name,
|
||||
}
|
||||
|
||||
discoveryPorts := []state.DiscoveryPort{dp}
|
||||
|
||||
ports := state.Ports{
|
||||
DiscoveryPorts: discoveryPorts,
|
||||
}
|
||||
|
||||
return state.DiscoveryInfo{
|
||||
Ports: ports,
|
||||
func withPort(proto string, port int, name string) func(port *state.DiscoveryPort) {
|
||||
return func(p *state.DiscoveryPort) {
|
||||
p.Protocol = proto
|
||||
p.Number = port
|
||||
p.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func setDiscoveryPorts(proto1 string, port1 int, name1 string, proto2 string, port2 int, name2 string) state.DiscoveryInfo {
|
||||
func withPortTCP(port int, name string) func(port *state.DiscoveryPort) {
|
||||
return withPort("TCP", port, name)
|
||||
}
|
||||
|
||||
dp1 := state.DiscoveryPort{
|
||||
Protocol: proto1,
|
||||
Number: port1,
|
||||
Name: name1,
|
||||
func withStatus(ops ...func(*state.Status)) func(*state.Task) {
|
||||
return func(task *state.Task) {
|
||||
st := &state.Status{}
|
||||
for _, op := range ops {
|
||||
op(st)
|
||||
}
|
||||
task.Statuses = append(task.Statuses, *st)
|
||||
}
|
||||
|
||||
dp2 := state.DiscoveryPort{
|
||||
Protocol: proto2,
|
||||
Number: port2,
|
||||
Name: name2,
|
||||
}
|
||||
|
||||
discoveryPorts := []state.DiscoveryPort{dp1, dp2}
|
||||
|
||||
ports := state.Ports{
|
||||
DiscoveryPorts: discoveryPorts,
|
||||
}
|
||||
|
||||
return state.DiscoveryInfo{
|
||||
Ports: ports,
|
||||
}
|
||||
func withDefaultStatus(ops ...func(*state.Status)) func(*state.Task) {
|
||||
return func(task *state.Task) {
|
||||
for _, op := range ops {
|
||||
st := &state.Status{
|
||||
State: "TASK_RUNNING",
|
||||
Healthy: Bool(true),
|
||||
}
|
||||
op(st)
|
||||
task.Statuses = append(task.Statuses, *st)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setState(st string) statusOpt {
|
||||
return func(s *state.Status) {
|
||||
s.State = st
|
||||
func withHealthy(st bool) func(*state.Status) {
|
||||
return func(status *state.Status) {
|
||||
status.Healthy = Bool(st)
|
||||
}
|
||||
}
|
||||
|
||||
func setHealthy(b bool) statusOpt {
|
||||
return func(s *state.Status) {
|
||||
s.Healthy = &b
|
||||
func withState(st string) func(*state.Status) {
|
||||
return func(status *state.Status) {
|
||||
status.State = st
|
||||
}
|
||||
}
|
||||
|
||||
func withLabel(key, value string) func(*state.Task) {
|
||||
return func(task *state.Task) {
|
||||
lbl := state.Label{Key: key, Value: value}
|
||||
task.Labels = append(task.Labels, lbl)
|
||||
}
|
||||
}
|
||||
|
||||
func Bool(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue