Fix support for macvlan driver in docker provider

This commit is contained in:
Michael 2018-02-12 17:50:05 +01:00 committed by Traefiker Bot
parent 38a4c80995
commit dd873fbeee
4 changed files with 225 additions and 101 deletions

View file

@ -95,6 +95,25 @@ func taskSlot(slot int) func(*swarm.Task) {
} }
} }
func taskNetworkAttachment(id string, name string, driver string, addresses []string) func(*swarm.Task) {
return func(task *swarm.Task) {
task.NetworksAttachments = append(task.NetworksAttachments, swarm.NetworkAttachment{
Network: swarm.Network{
ID: id,
Spec: swarm.NetworkSpec{
Annotations: swarm.Annotations{
Name: name,
},
DriverConfiguration: &swarm.Driver{
Name: driver,
},
},
},
Addresses: addresses,
})
}
}
func taskStatus(ops ...func(*swarm.TaskStatus)) func(*swarm.Task) { func taskStatus(ops ...func(*swarm.TaskStatus)) func(*swarm.Task) {
return func(task *swarm.Task) { return func(task *swarm.Task) {
status := &swarm.TaskStatus{} status := &swarm.TaskStatus{}
@ -113,6 +132,14 @@ func taskState(state swarm.TaskState) func(*swarm.TaskStatus) {
} }
} }
func taskContainerStatus(id string) func(*swarm.TaskStatus) {
return func(status *swarm.TaskStatus) {
status.ContainerStatus = swarm.ContainerStatus{
ContainerID: id,
}
}
}
func swarmService(ops ...func(*swarm.Service)) swarm.Service { func swarmService(ops ...func(*swarm.Service)) swarm.Service {
service := &swarm.Service{ service := &swarm.Service{
ID: "serviceID", ID: "serviceID",

View file

@ -1,7 +1,6 @@
package docker package docker
import ( import (
"reflect"
"strconv" "strconv"
"testing" "testing"
"time" "time"
@ -473,70 +472,6 @@ func TestSwarmTraefikFilter(t *testing.T) {
} }
} }
func TestSwarmTaskParsing(t *testing.T) {
testCases := []struct {
service swarm.Service
tasks []swarm.Task
isGlobalSVC bool
expectedNames map[string]string
networks map[string]*docker.NetworkResource
}{
{
service: swarmService(serviceName("container")),
tasks: []swarm.Task{
swarmTask("id1", taskSlot(1)),
swarmTask("id2", taskSlot(2)),
swarmTask("id3", taskSlot(3)),
},
isGlobalSVC: false,
expectedNames: map[string]string{
"id1": "container.1",
"id2": "container.2",
"id3": "container.3",
},
networks: map[string]*docker.NetworkResource{
"1": {
Name: "foo",
},
},
},
{
service: swarmService(serviceName("container")),
tasks: []swarm.Task{
swarmTask("id1"),
swarmTask("id2"),
swarmTask("id3"),
},
isGlobalSVC: true,
expectedNames: map[string]string{
"id1": "container.id1",
"id2": "container.id2",
"id3": "container.id3",
},
networks: map[string]*docker.NetworkResource{
"1": {
Name: "foo",
},
},
},
}
for caseID, test := range testCases {
test := test
t.Run(strconv.Itoa(caseID), func(t *testing.T) {
t.Parallel()
dData := parseService(test.service, test.networks)
for _, task := range test.tasks {
taskDockerData := parseTasks(task, dData, map[string]*docker.NetworkResource{}, test.isGlobalSVC)
if !reflect.DeepEqual(taskDockerData.Name, test.expectedNames[task.ID]) {
t.Errorf("expect %v, got %v", test.expectedNames[task.ID], taskDockerData.Name)
}
}
})
}
}
func TestSwarmGetFuncStringLabel(t *testing.T) { func TestSwarmGetFuncStringLabel(t *testing.T) {
testCases := []struct { testCases := []struct {
service swarm.Service service swarm.Service

View file

@ -260,19 +260,27 @@ func listContainers(ctx context.Context, dockerClient client.ContainerAPIClient)
var containersInspected []dockerData var containersInspected []dockerData
// get inspect containers // get inspect containers
for _, container := range containerList { for _, container := range containerList {
containerInspected, err := dockerClient.ContainerInspect(ctx, container.ID) dData := inspectContainers(ctx, dockerClient, container.ID)
if len(dData.Name) > 0 {
containersInspected = append(containersInspected, dData)
}
}
return containersInspected, nil
}
func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClient, containerID string) dockerData {
dData := dockerData{}
containerInspected, err := dockerClient.ContainerInspect(ctx, containerID)
if err != nil { if err != nil {
log.Warnf("Failed to inspect container %s, error: %s", container.ID, err) log.Warnf("Failed to inspect container %s, error: %s", containerID, err)
} else { } else {
// This condition is here to avoid to have empty IP https://github.com/containous/traefik/issues/2459 // This condition is here to avoid to have empty IP https://github.com/containous/traefik/issues/2459
// We register only container which are running // We register only container which are running
if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running { if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running {
dData := parseContainer(containerInspected) dData = parseContainer(containerInspected)
containersInspected = append(containersInspected, dData)
} }
} }
} return dData
return containersInspected, nil
} }
func parseContainer(container dockertypes.ContainerJSON) dockerData { func parseContainer(container dockertypes.ContainerJSON) dockerData {
@ -388,6 +396,7 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes
for _, virtualIP := range service.Endpoint.VirtualIPs { for _, virtualIP := range service.Endpoint.VirtualIPs {
networkService := networkMap[virtualIP.NetworkID] networkService := networkMap[virtualIP.NetworkID]
if networkService != nil { if networkService != nil {
if len(virtualIP.Addr) > 0 {
ip, _, _ := net.ParseCIDR(virtualIP.Addr) ip, _, _ := net.ParseCIDR(virtualIP.Addr)
network := &networkData{ network := &networkData{
Name: networkService.Name, Name: networkService.Name,
@ -395,6 +404,9 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes
Addr: ip.String(), Addr: ip.String(),
} }
dData.NetworkSettings.Networks[network.Name] = network dData.NetworkSettings.Networks[network.Name] = network
} else {
log.Debugf("No virtual IPs found in network %s", virtualIP.NetworkID)
}
} else { } else {
log.Debugf("Network not found, id: %s", virtualIP.NetworkID) log.Debugf("Network not found, id: %s", virtualIP.NetworkID)
} }
@ -421,12 +433,15 @@ func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID str
continue continue
} }
dData := parseTasks(task, serviceDockerData, networkMap, isGlobalSvc) dData := parseTasks(task, serviceDockerData, networkMap, isGlobalSvc)
if len(dData.NetworkSettings.Networks) > 0 {
dockerDataList = append(dockerDataList, dData) dockerDataList = append(dockerDataList, dData)
} }
}
return dockerDataList, err return dockerDataList, err
} }
func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap map[string]*dockertypes.NetworkResource, isGlobalSvc bool) dockerData { func parseTasks(task swarmtypes.Task, serviceDockerData dockerData,
networkMap map[string]*dockertypes.NetworkResource, isGlobalSvc bool) dockerData {
dData := dockerData{ dData := dockerData{
ServiceName: serviceDockerData.Name, ServiceName: serviceDockerData.Name,
Name: serviceDockerData.Name + "." + strconv.Itoa(task.Slot), Name: serviceDockerData.Name + "." + strconv.Itoa(task.Slot),
@ -442,6 +457,7 @@ func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap m
dData.NetworkSettings.Networks = make(map[string]*networkData) dData.NetworkSettings.Networks = make(map[string]*networkData)
for _, virtualIP := range task.NetworksAttachments { for _, virtualIP := range task.NetworksAttachments {
if networkService, present := networkMap[virtualIP.Network.ID]; present { if networkService, present := networkMap[virtualIP.Network.ID]; present {
if len(virtualIP.Addresses) > 0 {
// Not sure about this next loop - when would a task have multiple IP's for the same network? // Not sure about this next loop - when would a task have multiple IP's for the same network?
for _, addr := range virtualIP.Addresses { for _, addr := range virtualIP.Addresses {
ip, _, _ := net.ParseCIDR(addr) ip, _, _ := net.ParseCIDR(addr)
@ -452,6 +468,9 @@ func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap m
} }
dData.NetworkSettings.Networks[network.Name] = network dData.NetworkSettings.Networks[network.Name] = network
} }
} else {
log.Debugf("No IP addresses found for network %s", virtualIP.Network.ID)
}
} }
} }
} }

View file

@ -17,6 +17,7 @@ import (
type fakeTasksClient struct { type fakeTasksClient struct {
dockerclient.APIClient dockerclient.APIClient
tasks []swarm.Task tasks []swarm.Task
container dockertypes.ContainerJSON
err error err error
} }
@ -24,6 +25,10 @@ func (c *fakeTasksClient) TaskList(ctx context.Context, options dockertypes.Task
return c.tasks, c.err return c.tasks, c.err
} }
func (c *fakeTasksClient) ContainerInspect(ctx context.Context, container string) (dockertypes.ContainerJSON, error) {
return c.container, c.err
}
func TestListTasks(t *testing.T) { func TestListTasks(t *testing.T) {
testCases := []struct { testCases := []struct {
service swarm.Service service swarm.Service
@ -35,11 +40,30 @@ func TestListTasks(t *testing.T) {
{ {
service: swarmService(serviceName("container")), service: swarmService(serviceName("container")),
tasks: []swarm.Task{ tasks: []swarm.Task{
swarmTask("id1", taskSlot(1), taskStatus(taskState(swarm.TaskStateRunning))), swarmTask("id1",
swarmTask("id2", taskSlot(2), taskStatus(taskState(swarm.TaskStatePending))), taskSlot(1),
swarmTask("id3", taskSlot(3)), taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.1"}),
swarmTask("id4", taskSlot(4), taskStatus(taskState(swarm.TaskStateRunning))), taskStatus(taskState(swarm.TaskStateRunning)),
swarmTask("id5", taskSlot(5), taskStatus(taskState(swarm.TaskStateFailed))), ),
swarmTask("id2",
taskSlot(2),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.2"}),
taskStatus(taskState(swarm.TaskStatePending)),
),
swarmTask("id3",
taskSlot(3),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.3"}),
),
swarmTask("id4",
taskSlot(4),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.4"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
swarmTask("id5",
taskSlot(5),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.5"}),
taskStatus(taskState(swarm.TaskStateFailed)),
),
}, },
isGlobalSVC: false, isGlobalSVC: false,
expectedTasks: []string{ expectedTasks: []string{
@ -60,7 +84,7 @@ func TestListTasks(t *testing.T) {
t.Parallel() t.Parallel()
dockerData := parseService(test.service, test.networks) dockerData := parseService(test.service, test.networks)
dockerClient := &fakeTasksClient{tasks: test.tasks} dockerClient := &fakeTasksClient{tasks: test.tasks}
taskDockerData, _ := listTasks(context.Background(), dockerClient, test.service.ID, dockerData, map[string]*docker.NetworkResource{}, test.isGlobalSVC) taskDockerData, _ := listTasks(context.Background(), dockerClient, test.service.ID, dockerData, test.networks, test.isGlobalSVC)
if len(test.expectedTasks) != len(taskDockerData) { if len(test.expectedTasks) != len(taskDockerData) {
t.Errorf("expected tasks %v, got %v", spew.Sdump(test.expectedTasks), spew.Sdump(taskDockerData)) t.Errorf("expected tasks %v, got %v", spew.Sdump(test.expectedTasks), spew.Sdump(taskDockerData))
@ -203,8 +227,14 @@ func TestListServices(t *testing.T) {
withEndpointSpec(modeDNSSR)), withEndpointSpec(modeDNSSR)),
}, },
tasks: []swarm.Task{ tasks: []swarm.Task{
swarmTask("id1", taskStatus(taskState(swarm.TaskStateRunning))), swarmTask("id1",
swarmTask("id2", taskStatus(taskState(swarm.TaskStateRunning))), taskNetworkAttachment("yk6l57rfwizjzxxzftn4amaot", "network_name", "overlay", []string{"127.0.0.1"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
swarmTask("id2",
taskNetworkAttachment("yk6l57rfwizjzxxzftn4amaot", "network_name", "overlay", []string{"127.0.0.1"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
}, },
dockerVersion: "1.30", dockerVersion: "1.30",
networks: []dockertypes.NetworkResource{ networks: []dockertypes.NetworkResource{
@ -252,3 +282,116 @@ func TestListServices(t *testing.T) {
}) })
} }
} }
func TestSwarmTaskParsing(t *testing.T) {
testCases := []struct {
service swarm.Service
tasks []swarm.Task
isGlobalSVC bool
expected map[string]dockerData
networks map[string]*docker.NetworkResource
}{
{
service: swarmService(serviceName("container")),
tasks: []swarm.Task{
swarmTask("id1", taskSlot(1)),
swarmTask("id2", taskSlot(2)),
swarmTask("id3", taskSlot(3)),
},
isGlobalSVC: false,
expected: map[string]dockerData{
"id1": {
Name: "container.1",
},
"id2": {
Name: "container.2",
},
"id3": {
Name: "container.3",
},
},
networks: map[string]*docker.NetworkResource{
"1": {
Name: "foo",
},
},
},
{
service: swarmService(serviceName("container")),
tasks: []swarm.Task{
swarmTask("id1"),
swarmTask("id2"),
swarmTask("id3"),
},
isGlobalSVC: true,
expected: map[string]dockerData{
"id1": {
Name: "container.id1",
},
"id2": {
Name: "container.id2",
},
"id3": {
Name: "container.id3",
},
},
networks: map[string]*docker.NetworkResource{
"1": {
Name: "foo",
},
},
},
{
service: swarmService(
serviceName("container"),
withEndpointSpec(modeVIP),
withEndpoint(
virtualIP("1", ""),
),
),
tasks: []swarm.Task{
swarmTask(
"id1",
taskNetworkAttachment("1", "vlan", "macvlan", []string{"127.0.0.1"}),
taskStatus(
taskState(swarm.TaskStateRunning),
taskContainerStatus("c1"),
),
),
},
isGlobalSVC: true,
expected: map[string]dockerData{
"id1": {
Name: "container.id1",
NetworkSettings: networkSettings{
Networks: map[string]*networkData{
"vlan": {
Name: "vlan",
Addr: "10.11.12.13",
},
},
},
},
},
networks: map[string]*docker.NetworkResource{
"1": {
Name: "vlan",
},
},
},
}
for caseID, test := range testCases {
test := test
t.Run(strconv.Itoa(caseID), func(t *testing.T) {
t.Parallel()
dData := parseService(test.service, test.networks)
for _, task := range test.tasks {
taskDockerData := parseTasks(task, dData, test.networks, test.isGlobalSVC)
expected := test.expected[task.ID]
assert.Equal(t, expected.Name, taskDockerData.Name)
}
})
}
}