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) {
return func(task *swarm.Task) {
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 {
service := &swarm.Service{
ID: "serviceID",

View file

@ -1,7 +1,6 @@
package docker
import (
"reflect"
"strconv"
"testing"
"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) {
testCases := []struct {
service swarm.Service

View file

@ -260,19 +260,27 @@ func listContainers(ctx context.Context, dockerClient client.ContainerAPIClient)
var containersInspected []dockerData
// get inspect containers
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 {
log.Warnf("Failed to inspect container %s, error: %s", container.ID, err)
log.Warnf("Failed to inspect container %s, error: %s", containerID, err)
} else {
// This condition is here to avoid to have empty IP https://github.com/containous/traefik/issues/2459
// We register only container which are running
if containerInspected.ContainerJSONBase != nil && containerInspected.ContainerJSONBase.State != nil && containerInspected.ContainerJSONBase.State.Running {
dData := parseContainer(containerInspected)
containersInspected = append(containersInspected, dData)
dData = parseContainer(containerInspected)
}
}
}
return containersInspected, nil
return dData
}
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 {
networkService := networkMap[virtualIP.NetworkID]
if networkService != nil {
if len(virtualIP.Addr) > 0 {
ip, _, _ := net.ParseCIDR(virtualIP.Addr)
network := &networkData{
Name: networkService.Name,
@ -395,6 +404,9 @@ func parseService(service swarmtypes.Service, networkMap map[string]*dockertypes
Addr: ip.String(),
}
dData.NetworkSettings.Networks[network.Name] = network
} else {
log.Debugf("No virtual IPs found in network %s", virtualIP.NetworkID)
}
} else {
log.Debugf("Network not found, id: %s", virtualIP.NetworkID)
}
@ -421,12 +433,15 @@ func listTasks(ctx context.Context, dockerClient client.APIClient, serviceID str
continue
}
dData := parseTasks(task, serviceDockerData, networkMap, isGlobalSvc)
if len(dData.NetworkSettings.Networks) > 0 {
dockerDataList = append(dockerDataList, dData)
}
}
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{
ServiceName: serviceDockerData.Name,
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)
for _, virtualIP := range task.NetworksAttachments {
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?
for _, addr := range virtualIP.Addresses {
ip, _, _ := net.ParseCIDR(addr)
@ -452,6 +468,9 @@ func parseTasks(task swarmtypes.Task, serviceDockerData dockerData, networkMap m
}
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 {
dockerclient.APIClient
tasks []swarm.Task
container dockertypes.ContainerJSON
err error
}
@ -24,6 +25,10 @@ func (c *fakeTasksClient) TaskList(ctx context.Context, options dockertypes.Task
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) {
testCases := []struct {
service swarm.Service
@ -35,11 +40,30 @@ func TestListTasks(t *testing.T) {
{
service: swarmService(serviceName("container")),
tasks: []swarm.Task{
swarmTask("id1", taskSlot(1), taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id2", taskSlot(2), taskStatus(taskState(swarm.TaskStatePending))),
swarmTask("id3", taskSlot(3)),
swarmTask("id4", taskSlot(4), taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id5", taskSlot(5), taskStatus(taskState(swarm.TaskStateFailed))),
swarmTask("id1",
taskSlot(1),
taskNetworkAttachment("1", "network1", "overlay", []string{"127.0.0.1"}),
taskStatus(taskState(swarm.TaskStateRunning)),
),
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,
expectedTasks: []string{
@ -60,7 +84,7 @@ func TestListTasks(t *testing.T) {
t.Parallel()
dockerData := parseService(test.service, test.networks)
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) {
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)),
},
tasks: []swarm.Task{
swarmTask("id1", taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id2", taskStatus(taskState(swarm.TaskStateRunning))),
swarmTask("id1",
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",
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)
}
})
}
}