From 5eb23ea8210d27a6130d24ee6cc8c9070bfda65c Mon Sep 17 00:00:00 2001 From: Leo Antunes Date: Sat, 25 Aug 2018 13:25:47 +0200 Subject: [PATCH] skip "bridge" and "none" nets; add some tests --- docker.go | 18 +++- docker_test.go | 263 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 279 insertions(+), 2 deletions(-) create mode 100644 docker_test.go diff --git a/docker.go b/docker.go index 8921601..009fbca 100644 --- a/docker.go +++ b/docker.go @@ -18,6 +18,9 @@ type dockerClienter interface { ContainerList(context.Context, types.ContainerListOptions) ([]types.Container, error) ContainerInspect(context.Context, string) (types.ContainerJSON, error) Events(context.Context, types.EventsOptions) (<-chan events.Message, <-chan error) +} + +type dockerClientPinger interface { Ping(context.Context) (types.Ping, error) } @@ -57,15 +60,26 @@ func getIPsToNames(client dockerClienter, id string) (ipsToNamesMap, error) { } for netName, netInfo := range containerFull.NetworkSettings.Networks { + if netName == "none" { + continue + } + names := make([]string, 0, 4) // 4 is worst-case size if container in a compose project (see below) + maybeAppendNet := func(names []string, name string) []string { + if netName != "bridge" { + return append(names, fmt.Sprintf("%s.%s", name, netName)) + } + return names + } + appendNames := func(names []string, name string) []string { log.Debugf("found base name %s with IP %s", name, netInfo.IPAddress) names = append(names, fmt.Sprintf("%s", name)) - names = append(names, fmt.Sprintf("%s.%s", name, netName)) + names = maybeAppendNet(names, name) if proj, ok := containerFull.Config.Labels["com.docker.compose.project"]; ok { names = append(names, fmt.Sprintf("%s.%s", name, proj)) - names = append(names, fmt.Sprintf("%s.%s.%s", name, proj, netName)) + names = maybeAppendNet(names, fmt.Sprintf("%s.%s", name, proj)) } return names } diff --git a/docker_test.go b/docker_test.go new file mode 100644 index 0000000..1c3de38 --- /dev/null +++ b/docker_test.go @@ -0,0 +1,263 @@ +package main + +import ( + "context" + "errors" + "io/ioutil" + "reflect" + "testing" + + "docker.io/go-docker/api/types" + "docker.io/go-docker/api/types/container" + "docker.io/go-docker/api/types/events" + "docker.io/go-docker/api/types/network" + log "github.com/sirupsen/logrus" +) + +func init() { + log.SetOutput(ioutil.Discard) +} + +type testClient struct{} + +func (testClient) ContainerList(_ context.Context, _ types.ContainerListOptions) ([]types.Container, error) { + return []types.Container{ + { + ID: "111", + Names: []string{ + "/someservice", + }, + }, + { + ID: "222", + Names: []string{ + "/someproject_someservice_1", + }, + }, + { + ID: "333", + Names: []string{ + "/someotherproject_someotherservice_1", + }, + }, + { + ID: "444", + Names: []string{ + "/some_nonnetworked_service", + }, + }, + }, nil +} + +func (testClient) ContainerInspect(_ context.Context, ID string) (types.ContainerJSON, error) { + switch ID { + case "111": + return types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{Name: "service1"}, + Config: &container.Config{Labels: map[string]string{}}, + NetworkSettings: &types.NetworkSettings{ + Networks: map[string]*network.EndpointSettings{ + "bridge": { + IPAddress: "1.2.3.4", + Aliases: []string{ + "somealias", + }, + }, + }, + }, + }, nil + case "222": + return types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{Name: "service2"}, + Config: &container.Config{Labels: map[string]string{ + "com.docker.compose.project": "someproject", + }}, + NetworkSettings: &types.NetworkSettings{ + Networks: map[string]*network.EndpointSettings{ + "somenetwork": { + IPAddress: "2.3.4.5", + Aliases: []string{ + "somealias1", + "nonuniquealias", + }, + }, + }, + }, + }, nil + case "333": + return types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{Name: "service3"}, + Config: &container.Config{Labels: map[string]string{ + "com.docker.compose.project": "someotherproject", + }}, + NetworkSettings: &types.NetworkSettings{ + Networks: map[string]*network.EndpointSettings{ + "someothernetwork": { + IPAddress: "3.4.5.6", + Aliases: []string{ + "someotheralias1", + "nonuniquealias", + }, + }, + "somesecondarynetwork": { + IPAddress: "4.5.6.7", + Aliases: []string{ + "somesecondaryalias1", + }, + }, + }, + }, + }, nil + case "444": + return types.ContainerJSON{ + ContainerJSONBase: &types.ContainerJSONBase{Name: "service4"}, + Config: &container.Config{Labels: map[string]string{}}, + NetworkSettings: &types.NetworkSettings{ + Networks: map[string]*network.EndpointSettings{ + "none": {}, + }, + }, + }, nil + default: + panic("whaaa?") + } +} + +func (testClient) Events(context.Context, types.EventsOptions) (<-chan events.Message, <-chan error) { + return nil, nil +} + +func (testClient) Ping(context.Context) (types.Ping, error) { + return types.Ping{}, nil +} + +type workingPinger struct{} + +func (workingPinger) Ping(_ context.Context) (types.Ping, error) { + return types.Ping{}, nil +} + +// not safe to use same pinger in multiple parallel tests! +type delayedPinger struct{ counter, limit int } + +func (wp *delayedPinger) Ping(_ context.Context) (types.Ping, error) { + // some arbitrary number of failures + if wp.counter >= wp.limit { + return types.Ping{}, nil + } + wp.counter++ + return types.Ping{}, errors.New("not working yet") +} + +func Test_getIPsToNames(t *testing.T) { + type args struct { + client dockerClienter + id string + } + tests := []struct { + name string + args args + want ipsToNamesMap + wantErr bool + }{ + {"simple query1", args{testClient{}, "111"}, ipsToNamesMap{ + "1.2.3.4": []string{ + "service1", "somealias", + }, + }, false}, + {"query with aliases and projects", args{testClient{}, "222"}, ipsToNamesMap{ + "2.3.4.5": []string{ + "service2", "service2.somenetwork", "service2.someproject", "service2.someproject.somenetwork", + "somealias1", "somealias1.somenetwork", "somealias1.someproject", "somealias1.someproject.somenetwork", + "nonuniquealias", "nonuniquealias.somenetwork", "nonuniquealias.someproject", "nonuniquealias.someproject.somenetwork", + }, + }, false}, + {"query with 2 networks", args{testClient{}, "333"}, ipsToNamesMap{ + "3.4.5.6": []string{ + "service3", "service3.someothernetwork", "service3.someotherproject", "service3.someotherproject.someothernetwork", + "someotheralias1", "someotheralias1.someothernetwork", "someotheralias1.someotherproject", "someotheralias1.someotherproject.someothernetwork", + "nonuniquealias", "nonuniquealias.someothernetwork", "nonuniquealias.someotherproject", "nonuniquealias.someotherproject.someothernetwork", + }, + "4.5.6.7": []string{ + "service3", "service3.somesecondarynetwork", "service3.someotherproject", "service3.someotherproject.somesecondarynetwork", + "somesecondaryalias1", "somesecondaryalias1.somesecondarynetwork", "somesecondaryalias1.someotherproject", "somesecondaryalias1.someotherproject.somesecondarynetwork", + }, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getIPsToNames(tt.args.client, tt.args.id) + if (err != nil) != tt.wantErr { + t.Errorf("getIPsToNames() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getIPsToNames():\n%v\nwant:\n%v", got, tt.want) + } + }) + } +} + +func Test_getAllIPsToNames(t *testing.T) { + type args struct { + client dockerClienter + } + tests := []struct { + name string + args args + want ipsToNamesMap + wantErr bool + }{ + {"simple query1", args{testClient{}}, ipsToNamesMap{ + "1.2.3.4": []string{"service1", "somealias"}, + "2.3.4.5": []string{ + "service2", "service2.somenetwork", "service2.someproject", "service2.someproject.somenetwork", + "somealias1", "somealias1.somenetwork", "somealias1.someproject", "somealias1.someproject.somenetwork", + "nonuniquealias", "nonuniquealias.somenetwork", "nonuniquealias.someproject", "nonuniquealias.someproject.somenetwork", + }, + "3.4.5.6": []string{ + "service3", "service3.someothernetwork", "service3.someotherproject", "service3.someotherproject.someothernetwork", + "someotheralias1", "someotheralias1.someothernetwork", "someotheralias1.someotherproject", "someotheralias1.someotherproject.someothernetwork", + "nonuniquealias", "nonuniquealias.someothernetwork", "nonuniquealias.someotherproject", "nonuniquealias.someotherproject.someothernetwork", + }, + "4.5.6.7": []string{ + "service3", "service3.somesecondarynetwork", "service3.someotherproject", "service3.someotherproject.somesecondarynetwork", + "somesecondaryalias1", "somesecondaryalias1.somesecondarynetwork", "somesecondaryalias1.someotherproject", "somesecondaryalias1.someotherproject.somesecondarynetwork", + }, + }, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := getAllIPsToNames(tt.args.client) + if (err != nil) != tt.wantErr { + t.Errorf("getAllIPsToNames() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("getAllIPsToNames() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_waitForConnection(t *testing.T) { + type args struct { + client dockerClientPinger + } + tests := []struct { + name string + args args + long bool // whether this test is "long-running" + }{ + {"connection working", args{workingPinger{}}, false}, + {"delayed connection", args{&delayedPinger{limit: 5}}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.long && testing.Short() { + t.Skip() + } + waitForConnection(tt.args.client) + }) + } +}