From d045bd44e04110919d8ec7c6cf71c82e54b280e1 Mon Sep 17 00:00:00 2001 From: Dong Chen Date: Tue, 22 Mar 2016 01:49:06 -0700 Subject: [PATCH] Skip redundant endpoints in "network inspect" Signed-off-by: Dong Chen --- api/handlers.go | 5 +- cluster/network.go | 28 +++++++++++ cluster/network_test.go | 109 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/api/handlers.go b/api/handlers.go index 8a051f6ee1..dc76ee8330 100644 --- a/api/handlers.go +++ b/api/handlers.go @@ -271,8 +271,11 @@ func getNetworks(c *context, w http.ResponseWriter, r *http.Request) { func getNetwork(c *context, w http.ResponseWriter, r *http.Request) { var id = mux.Vars(r)["networkid"] if network := c.cluster.Networks().Uniq().Get(id); network != nil { + // there could be duplicate container endpoints in network, need to remove redundant + // see https://github.com/docker/swarm/issues/1969 + cleanNetwork := network.RemoveDuplicateEndpoints() w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(network) + json.NewEncoder(w).Encode(cleanNetwork) return } httpError(w, fmt.Sprintf("No such network: %s", id), http.StatusNotFound) diff --git a/cluster/network.go b/cluster/network.go index c705151385..9663a0af99 100644 --- a/cluster/network.go +++ b/cluster/network.go @@ -78,6 +78,34 @@ func (networks Networks) Filter(names []string, ids []string, types []string) Ne return out } +// RemoveDuplicateEndpoints returns a copy of input network +// where duplicate container endpoints in the network are removed. +// See https://github.com/docker/swarm/issues/1969 +// This function should be disabled when the bug is fixed in Docker network +func (network *Network) RemoveDuplicateEndpoints() *Network { + // build a map from endpointID -> endpointIndex + endpointMap := make(map[string]string) + // traverse the endpoints to find the correct endpointIndex for each endpointID + for endpointIndex, endpointResource := range network.NetworkResource.Containers { + endpointID := endpointResource.EndpointID + // if this endpointID doesn't exist yet, add it + // if this endpointID exists, but endpointIndex is not a duplicate, use + // this endpointIndex + if _, ok := endpointMap[endpointID]; !ok || !strings.HasPrefix(endpointIndex, "ep-") { + endpointMap[endpointID] = endpointIndex + } + } + // Make a copy of the network + netCopy := *network + // clean up existing endpoints + netCopy.Containers = make(map[string]dockerclient.EndpointResource) + // add the endpoint index from endpointMap + for _, index := range endpointMap { + netCopy.Containers[index] = network.Containers[index] + } + return &netCopy +} + // Get returns a network using its ID or Name func (networks Networks) Get(IDOrName string) *Network { // Abort immediately if the name is empty. diff --git a/cluster/network_test.go b/cluster/network_test.go index 577fa6e8e4..9ce73f6861 100644 --- a/cluster/network_test.go +++ b/cluster/network_test.go @@ -80,3 +80,112 @@ func TestNetworkUniq(t *testing.T) { local3 := networks.Uniq().Get("local3") assert.Nil(t, local3) } + +func TestRemoveDuplicateEndpoints(t *testing.T) { + engine1 := &Engine{ID: "id1"} + network := Network{ + dockerclient.NetworkResource{ + ID: "global", + Name: "voteappbase_voteapp", + Containers: map[string]dockerclient.EndpointResource{ + "028771f7f6a54c486d441ecfc92aad68e0836a1f0a5a0c227c514f14848e2b54": { + Name: "voteappbase_worker_1", + EndpointID: "49f621862a0659f462870a6cd15874da44592e399f41da2a3019d81b7427315b", + MacAddress: "02:42:0a:00:02:04", + IPv4Address: "10.0.2.4/24", + IPv6Address: "", + }, + "6baa9c82be8fe731556b371312989e22fb9e67121698c94b4890dd554381d97b": { + Name: "voteappbase_voting-app_1", + EndpointID: "95e831e91a76c87a51b93217645fcaf093c209e2b6691493bfdc0cf2c39698d0", + MacAddress: "02:42:0a:00:02:05", + IPv4Address: "10.0.2.5/24", + IPv6Address: "", + }, + "995eccf14e33797b15c2c1ba67f68b7ced0754c42f096e02cc1c488a418f6126": { + Name: "db", + EndpointID: "e881cf9ba6dcc5495630d4e2ffba9178f7f4456f12938624562e2cf79888e6b4", + MacAddress: "02:42:0a:00:02:02", + IPv4Address: "10.0.2.2/24", + IPv6Address: "", + }, + "e4547f30d8dcd93a9c0da8b44e2a8f793eb01260aa478243605d137a90688611": { + Name: "voteappbase_result-app_1", + EndpointID: "d79ed2653731eccc968bd32da158bf74646511242d7a96f31fd350eda5b658cb", + MacAddress: "02:42:0a:00:02:06", + IPv4Address: "10.0.2.6/24", + IPv6Address: "", + }, + "ep-49f621862a0659f462870a6cd15874da44592e399f41da2a3019d81b7427315b": { + Name: "voteappbase_worker_1", + EndpointID: "49f621862a0659f462870a6cd15874da44592e399f41da2a3019d81b7427315b", + MacAddress: "02:42:0a:00:02:04", + IPv4Address: "10.0.2.4/24", + IPv6Address: "", + }, + "ep-95e831e91a76c87a51b93217645fcaf093c209e2b6691493bfdc0cf2c39698d0": { + Name: "voteappbase_voting-app_1", + EndpointID: "95e831e91a76c87a51b93217645fcaf093c209e2b6691493bfdc0cf2c39698d0", + MacAddress: "02:42:0a:00:02:05", + IPv4Address: "10.0.2.5/24", + IPv6Address: "", + }, + "ep-d79ed2653731eccc968bd32da158bf74646511242d7a96f31fd350eda5b658cb": { + Name: "voteappbase_result-app_1", + EndpointID: "d79ed2653731eccc968bd32da158bf74646511242d7a96f31fd350eda5b658cb", + MacAddress: "02:42:0a:00:02:06", + IPv4Address: "10.0.2.6/24", + IPv6Address: "", + }, + "ep-e881cf9ba6dcc5495630d4e2ffba9178f7f4456f12938624562e2cf79888e6b4": { + Name: "db", + EndpointID: "e881cf9ba6dcc5495630d4e2ffba9178f7f4456f12938624562e2cf79888e6b4", + MacAddress: "02:42:0a:00:02:02", + IPv4Address: "10.0.2.2/24", + IPv6Address: "", + }, + "ep-eaa1cf9ba6dcc5495630d4e2ffba917af7f4456f1293a624562e2cf79aaae6b4": { + Name: "db-stale", + EndpointID: "eaa1cf9ba6dcc5495630d4e2ffba917af7f4456f1293a624562e2cf79aaae6b4", + MacAddress: "02:42:0a:00:02:32", + IPv4Address: "10.0.2.33/24", + IPv6Address: "", + }, + "ep-f2e8540123a5b5894da462b8fd06de6394cb1a263392167b87e1b7195ec33055": { + Name: "redis", + EndpointID: "f2e8540123a5b5894da462b8fd06de6394cb1a263392167b87e1b7195ec33055", + MacAddress: "02:42:0a:00:02:03", + IPv4Address: "10.0.2.3/24", + IPv6Address: "", + }, + "f7e42305c1c50b145814d28508312e7374edbafebd8f04115606e58fde96f441": { + Name: "redis", + EndpointID: "f2e8540123a5b5894da462b8fd06de6394cb1a263392167b87e1b7195ec33055", + MacAddress: "02:42:0a:00:02:03", + IPv4Address: "10.0.2.3/24", + IPv6Address: "", + }, + }, + }, engine1} + + cleanNet := network.RemoveDuplicateEndpoints() + assert.Equal(t, len(cleanNet.Containers), 6) + + // good endpoints are preserved + resource, ok := cleanNet.Containers["028771f7f6a54c486d441ecfc92aad68e0836a1f0a5a0c227c514f14848e2b54"] + assert.True(t, ok) + assert.Equal(t, resource.EndpointID, "49f621862a0659f462870a6cd15874da44592e399f41da2a3019d81b7427315b") + + resource, ok = cleanNet.Containers["f7e42305c1c50b145814d28508312e7374edbafebd8f04115606e58fde96f441"] + assert.True(t, ok) + assert.Equal(t, resource.EndpointID, "f2e8540123a5b5894da462b8fd06de6394cb1a263392167b87e1b7195ec33055") + + // duplicate endpoint should be removed + resource, ok = cleanNet.Containers["ep-f2e8540123a5b5894da462b8fd06de6394cb1a263392167b87e1b7195ec33055"] + assert.False(t, ok) + + // stale endpoint is preserved + resource, ok = cleanNet.Containers["ep-eaa1cf9ba6dcc5495630d4e2ffba917af7f4456f1293a624562e2cf79aaae6b4"] + assert.True(t, ok) + assert.Equal(t, resource.EndpointID, "eaa1cf9ba6dcc5495630d4e2ffba917af7f4456f1293a624562e2cf79aaae6b4") +}