Update `FindNetwork` to address network name duplications
| ... | ... |
@@ -12,11 +12,11 @@ import ( |
| 12 | 12 |
// Backend is all the methods that need to be implemented |
| 13 | 13 |
// to provide network specific functionality. |
| 14 | 14 |
type Backend interface {
|
| 15 |
- FindNetwork(idName string) (libnetwork.Network, error) |
|
| 15 |
+ FindUniqueNetwork(idName string) (libnetwork.Network, error) |
|
| 16 | 16 |
GetNetworks() []libnetwork.Network |
| 17 | 17 |
CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) |
| 18 | 18 |
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error |
| 19 | 19 |
DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error |
| 20 |
- DeleteNetwork(name string) error |
|
| 20 |
+ DeleteNetwork(networkID string) error |
|
| 21 | 21 |
NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) |
| 22 | 22 |
} |
| ... | ... |
@@ -2,6 +2,7 @@ package network |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
+ "fmt" |
|
| 5 | 6 |
"net/http" |
| 6 | 7 |
"strconv" |
| 7 | 8 |
"strings" |
| ... | ... |
@@ -288,7 +289,12 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW |
| 288 | 288 |
return err |
| 289 | 289 |
} |
| 290 | 290 |
|
| 291 |
- return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig) |
|
| 291 |
+ // Always make sure there is no ambiguity with respect to the network ID/name |
|
| 292 |
+ nw, err := n.backend.FindUniqueNetwork(vars["id"]) |
|
| 293 |
+ if err != nil {
|
|
| 294 |
+ return err |
|
| 295 |
+ } |
|
| 296 |
+ return n.backend.ConnectContainerToNetwork(connect.Container, nw.ID(), connect.EndpointConfig) |
|
| 292 | 297 |
} |
| 293 | 298 |
|
| 294 | 299 |
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
| ... | ... |
@@ -312,15 +318,19 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter |
| 312 | 312 |
if err := httputils.ParseForm(r); err != nil {
|
| 313 | 313 |
return err |
| 314 | 314 |
} |
| 315 |
- if _, err := n.cluster.GetNetwork(vars["id"]); err == nil {
|
|
| 316 |
- if err = n.cluster.RemoveNetwork(vars["id"]); err != nil {
|
|
| 315 |
+ |
|
| 316 |
+ nw, err := n.findUniqueNetwork(vars["id"]) |
|
| 317 |
+ if err != nil {
|
|
| 318 |
+ return err |
|
| 319 |
+ } |
|
| 320 |
+ if nw.Scope == "swarm" {
|
|
| 321 |
+ if err = n.cluster.RemoveNetwork(nw.ID); err != nil {
|
|
| 322 |
+ return err |
|
| 323 |
+ } |
|
| 324 |
+ } else {
|
|
| 325 |
+ if err := n.backend.DeleteNetwork(nw.ID); err != nil {
|
|
| 317 | 326 |
return err |
| 318 | 327 |
} |
| 319 |
- w.WriteHeader(http.StatusNoContent) |
|
| 320 |
- return nil |
|
| 321 |
- } |
|
| 322 |
- if err := n.backend.DeleteNetwork(vars["id"]); err != nil {
|
|
| 323 |
- return err |
|
| 324 | 328 |
} |
| 325 | 329 |
w.WriteHeader(http.StatusNoContent) |
| 326 | 330 |
return nil |
| ... | ... |
@@ -518,3 +528,79 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr |
| 518 | 518 |
} |
| 519 | 519 |
return httputils.WriteJSON(w, http.StatusOK, pruneReport) |
| 520 | 520 |
} |
| 521 |
+ |
|
| 522 |
+// findUniqueNetwork will search network across different scopes (both local and swarm). |
|
| 523 |
+// NOTE: This findUniqueNetwork is different from FindUniqueNetwork in the daemon. |
|
| 524 |
+// In case multiple networks have duplicate names, return error. |
|
| 525 |
+// First find based on full ID, return immediately once one is found. |
|
| 526 |
+// If a network appears both in swarm and local, assume it is in local first |
|
| 527 |
+// For full name and partial ID, save the result first, and process later |
|
| 528 |
+// in case multiple records was found based on the same term |
|
| 529 |
+// TODO (yongtang): should we wrap with version here for backward compatibility? |
|
| 530 |
+func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) {
|
|
| 531 |
+ listByFullName := map[string]types.NetworkResource{}
|
|
| 532 |
+ listByPartialID := map[string]types.NetworkResource{}
|
|
| 533 |
+ |
|
| 534 |
+ nw := n.backend.GetNetworks() |
|
| 535 |
+ for _, network := range nw {
|
|
| 536 |
+ if network.ID() == term {
|
|
| 537 |
+ return *n.buildDetailedNetworkResources(network, false), nil |
|
| 538 |
+ |
|
| 539 |
+ } |
|
| 540 |
+ if network.Name() == term && !network.Info().Ingress() {
|
|
| 541 |
+ // No need to check the ID collision here as we are still in |
|
| 542 |
+ // local scope and the network ID is unique in this scope. |
|
| 543 |
+ listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, false) |
|
| 544 |
+ } |
|
| 545 |
+ if strings.HasPrefix(network.ID(), term) {
|
|
| 546 |
+ // No need to check the ID collision here as we are still in |
|
| 547 |
+ // local scope and the network ID is unique in this scope. |
|
| 548 |
+ listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, false) |
|
| 549 |
+ } |
|
| 550 |
+ } |
|
| 551 |
+ |
|
| 552 |
+ nr, _ := n.cluster.GetNetworks() |
|
| 553 |
+ for _, network := range nr {
|
|
| 554 |
+ if network.ID == term {
|
|
| 555 |
+ return network, nil |
|
| 556 |
+ } |
|
| 557 |
+ if network.Name == term {
|
|
| 558 |
+ // Check the ID collision as we are in swarm scope here, and |
|
| 559 |
+ // the map (of the listByFullName) may have already had a |
|
| 560 |
+ // network with the same ID (from local scope previously) |
|
| 561 |
+ if _, ok := listByFullName[network.ID]; !ok {
|
|
| 562 |
+ listByFullName[network.ID] = network |
|
| 563 |
+ } |
|
| 564 |
+ } |
|
| 565 |
+ if strings.HasPrefix(network.ID, term) {
|
|
| 566 |
+ // Check the ID collision as we are in swarm scope here, and |
|
| 567 |
+ // the map (of the listByPartialID) may have already had a |
|
| 568 |
+ // network with the same ID (from local scope previously) |
|
| 569 |
+ if _, ok := listByPartialID[network.ID]; !ok {
|
|
| 570 |
+ listByPartialID[network.ID] = network |
|
| 571 |
+ } |
|
| 572 |
+ } |
|
| 573 |
+ } |
|
| 574 |
+ |
|
| 575 |
+ // Find based on full name, returns true only if no duplicates |
|
| 576 |
+ if len(listByFullName) == 1 {
|
|
| 577 |
+ for _, v := range listByFullName {
|
|
| 578 |
+ return v, nil |
|
| 579 |
+ } |
|
| 580 |
+ } |
|
| 581 |
+ if len(listByFullName) > 1 {
|
|
| 582 |
+ return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
|
|
| 583 |
+ } |
|
| 584 |
+ |
|
| 585 |
+ // Find based on partial ID, returns true only if no duplicates |
|
| 586 |
+ if len(listByPartialID) == 1 {
|
|
| 587 |
+ for _, v := range listByPartialID {
|
|
| 588 |
+ return v, nil |
|
| 589 |
+ } |
|
| 590 |
+ } |
|
| 591 |
+ if len(listByPartialID) > 1 {
|
|
| 592 |
+ return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
|
|
| 593 |
+ } |
|
| 594 |
+ |
|
| 595 |
+ return types.NetworkResource{}, libnetwork.ErrNoSuchNetwork(term)
|
|
| 596 |
+} |
| ... | ... |
@@ -27,8 +27,8 @@ import ( |
| 27 | 27 |
// Backend defines the executor component for a swarm agent. |
| 28 | 28 |
type Backend interface {
|
| 29 | 29 |
CreateManagedNetwork(clustertypes.NetworkCreateRequest) error |
| 30 |
- DeleteManagedNetwork(name string) error |
|
| 31 |
- FindNetwork(idName string) (libnetwork.Network, error) |
|
| 30 |
+ DeleteManagedNetwork(networkID string) error |
|
| 31 |
+ FindUniqueNetwork(idName string) (libnetwork.Network, error) |
|
| 32 | 32 |
SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
|
| 33 | 33 |
ReleaseIngress() (<-chan struct{}, error)
|
| 34 | 34 |
PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error |
| ... | ... |
@@ -143,8 +143,8 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
|
| 143 | 143 |
} |
| 144 | 144 |
|
| 145 | 145 |
func (c *containerAdapter) createNetworks(ctx context.Context) error {
|
| 146 |
- for _, network := range c.container.networks() {
|
|
| 147 |
- ncr, err := c.container.networkCreateRequest(network) |
|
| 146 |
+ for name := range c.container.networksAttachments {
|
|
| 147 |
+ ncr, err := c.container.networkCreateRequest(name) |
|
| 148 | 148 |
if err != nil {
|
| 149 | 149 |
return err |
| 150 | 150 |
} |
| ... | ... |
@@ -162,15 +162,15 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error {
|
| 162 | 162 |
} |
| 163 | 163 |
|
| 164 | 164 |
func (c *containerAdapter) removeNetworks(ctx context.Context) error {
|
| 165 |
- for _, nid := range c.container.networks() {
|
|
| 166 |
- if err := c.backend.DeleteManagedNetwork(nid); err != nil {
|
|
| 165 |
+ for name, v := range c.container.networksAttachments {
|
|
| 166 |
+ if err := c.backend.DeleteManagedNetwork(v.Network.ID); err != nil {
|
|
| 167 | 167 |
switch err.(type) {
|
| 168 | 168 |
case *libnetwork.ActiveEndpointsError: |
| 169 | 169 |
continue |
| 170 | 170 |
case libnetwork.ErrNoSuchNetwork: |
| 171 | 171 |
continue |
| 172 | 172 |
default: |
| 173 |
- log.G(ctx).Errorf("network %s remove failed: %v", nid, err)
|
|
| 173 |
+ log.G(ctx).Errorf("network %s remove failed: %v", name, err)
|
|
| 174 | 174 |
return err |
| 175 | 175 |
} |
| 176 | 176 |
} |
| ... | ... |
@@ -507,7 +507,7 @@ func getEndpointConfig(na *api.NetworkAttachment, b executorpkg.Backend) *networ |
| 507 | 507 |
DriverOpts: na.DriverAttachmentOpts, |
| 508 | 508 |
} |
| 509 | 509 |
if v, ok := na.Network.Spec.Annotations.Labels["com.docker.swarm.predefined"]; ok && v == "true" {
|
| 510 |
- if ln, err := b.FindNetwork(na.Network.Spec.Annotations.Name); err == nil {
|
|
| 510 |
+ if ln, err := b.FindUniqueNetwork(na.Network.Spec.Annotations.Name); err == nil {
|
|
| 511 | 511 |
n.NetworkID = ln.ID() |
| 512 | 512 |
} |
| 513 | 513 |
} |
| ... | ... |
@@ -575,19 +575,6 @@ func (c *containerConfig) serviceConfig() *clustertypes.ServiceConfig {
|
| 575 | 575 |
return svcCfg |
| 576 | 576 |
} |
| 577 | 577 |
|
| 578 |
-// networks returns a list of network names attached to the container. The |
|
| 579 |
-// returned name can be used to lookup the corresponding network create |
|
| 580 |
-// options. |
|
| 581 |
-func (c *containerConfig) networks() []string {
|
|
| 582 |
- var networks []string |
|
| 583 |
- |
|
| 584 |
- for name := range c.networksAttachments {
|
|
| 585 |
- networks = append(networks, name) |
|
| 586 |
- } |
|
| 587 |
- |
|
| 588 |
- return networks |
|
| 589 |
-} |
|
| 590 |
- |
|
| 591 | 578 |
func (c *containerConfig) networkCreateRequest(name string) (clustertypes.NetworkCreateRequest, error) {
|
| 592 | 579 |
na, ok := c.networksAttachments[name] |
| 593 | 580 |
if !ok {
|
| ... | ... |
@@ -292,7 +292,7 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control |
| 292 | 292 |
for i, n := range networks {
|
| 293 | 293 |
apiNetwork, err := getNetwork(ctx, client, n.Target) |
| 294 | 294 |
if err != nil {
|
| 295 |
- ln, _ := c.config.Backend.FindNetwork(n.Target) |
|
| 295 |
+ ln, _ := c.config.Backend.FindUniqueNetwork(n.Target) |
|
| 296 | 296 |
if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) {
|
| 297 | 297 |
// Need to retrieve the corresponding predefined swarm network |
| 298 | 298 |
// and use its id for the request. |
| ... | ... |
@@ -251,8 +251,8 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li |
| 251 | 251 |
return runconfig.ErrConflictHostNetwork |
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 |
- for s := range container.NetworkSettings.Networks {
|
|
| 255 |
- sn, err := daemon.FindNetwork(s) |
|
| 254 |
+ for s, v := range container.NetworkSettings.Networks {
|
|
| 255 |
+ sn, err := daemon.FindUniqueNetwork(getNetworkID(s, v.EndpointSettings)) |
|
| 256 | 256 |
if err != nil {
|
| 257 | 257 |
continue |
| 258 | 258 |
} |
| ... | ... |
@@ -308,8 +308,8 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
| 308 | 308 |
|
| 309 | 309 |
// Find if container is connected to the default bridge network |
| 310 | 310 |
var n libnetwork.Network |
| 311 |
- for name := range container.NetworkSettings.Networks {
|
|
| 312 |
- sn, err := daemon.FindNetwork(name) |
|
| 311 |
+ for name, v := range container.NetworkSettings.Networks {
|
|
| 312 |
+ sn, err := daemon.FindUniqueNetwork(getNetworkID(name, v.EndpointSettings)) |
|
| 313 | 313 |
if err != nil {
|
| 314 | 314 |
continue |
| 315 | 315 |
} |
| ... | ... |
@@ -339,7 +339,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
|
| 339 | 339 |
} |
| 340 | 340 |
|
| 341 | 341 |
func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrName string, epConfig *networktypes.EndpointSettings) (libnetwork.Network, *networktypes.NetworkingConfig, error) {
|
| 342 |
- n, err := daemon.FindNetwork(idOrName) |
|
| 342 |
+ n, err := daemon.FindUniqueNetwork(getNetworkID(idOrName, epConfig)) |
|
| 343 | 343 |
if err != nil {
|
| 344 | 344 |
// We should always be able to find the network for a |
| 345 | 345 |
// managed container. |
| ... | ... |
@@ -377,16 +377,16 @@ func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrN |
| 377 | 377 |
// trigger attachment in the swarm cluster manager. |
| 378 | 378 |
if daemon.clusterProvider != nil {
|
| 379 | 379 |
var err error |
| 380 |
- config, err = daemon.clusterProvider.AttachNetwork(idOrName, container.ID, addresses) |
|
| 380 |
+ config, err = daemon.clusterProvider.AttachNetwork(getNetworkID(idOrName, epConfig), container.ID, addresses) |
|
| 381 | 381 |
if err != nil {
|
| 382 | 382 |
return nil, nil, err |
| 383 | 383 |
} |
| 384 | 384 |
} |
| 385 | 385 |
|
| 386 |
- n, err = daemon.FindNetwork(idOrName) |
|
| 386 |
+ n, err = daemon.FindUniqueNetwork(getNetworkID(idOrName, epConfig)) |
|
| 387 | 387 |
if err != nil {
|
| 388 | 388 |
if daemon.clusterProvider != nil {
|
| 389 |
- if err := daemon.clusterProvider.DetachNetwork(idOrName, container.ID); err != nil {
|
|
| 389 |
+ if err := daemon.clusterProvider.DetachNetwork(getNetworkID(idOrName, epConfig), container.ID); err != nil {
|
|
| 390 | 390 |
logrus.Warnf("Could not rollback attachment for container %s to network %s: %v", container.ID, idOrName, err)
|
| 391 | 391 |
} |
| 392 | 392 |
} |
| ... | ... |
@@ -437,7 +437,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai |
| 437 | 437 |
if mode.IsUserDefined() {
|
| 438 | 438 |
var err error |
| 439 | 439 |
|
| 440 |
- n, err = daemon.FindNetwork(networkName) |
|
| 440 |
+ n, err = daemon.FindUniqueNetwork(networkName) |
|
| 441 | 441 |
if err == nil {
|
| 442 | 442 |
networkName = n.Name() |
| 443 | 443 |
} |
| ... | ... |
@@ -797,7 +797,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName |
| 797 | 797 |
|
| 798 | 798 |
// ForceEndpointDelete deletes an endpoint from a network forcefully |
| 799 | 799 |
func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error {
|
| 800 |
- n, err := daemon.FindNetwork(networkName) |
|
| 800 |
+ n, err := daemon.FindUniqueNetwork(networkName) |
|
| 801 | 801 |
if err != nil {
|
| 802 | 802 |
return err |
| 803 | 803 |
} |
| ... | ... |
@@ -949,7 +949,7 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
|
| 949 | 949 |
|
| 950 | 950 |
var networks []libnetwork.Network |
| 951 | 951 |
for n, epSettings := range settings {
|
| 952 |
- if nw, err := daemon.FindNetwork(n); err == nil {
|
|
| 952 |
+ if nw, err := daemon.FindUniqueNetwork(getNetworkID(n, epSettings.EndpointSettings)); err == nil {
|
|
| 953 | 953 |
networks = append(networks, nw) |
| 954 | 954 |
} |
| 955 | 955 |
|
| ... | ... |
@@ -993,7 +993,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName |
| 993 | 993 |
return errRemovalContainer(container.ID) |
| 994 | 994 |
} |
| 995 | 995 |
|
| 996 |
- n, err := daemon.FindNetwork(idOrName) |
|
| 996 |
+ n, err := daemon.FindUniqueNetwork(idOrName) |
|
| 997 | 997 |
if err == nil && n != nil {
|
| 998 | 998 |
if err := daemon.updateNetworkConfig(container, n, endpointConfig, true); err != nil {
|
| 999 | 999 |
return err |
| ... | ... |
@@ -1016,7 +1016,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName |
| 1016 | 1016 |
|
| 1017 | 1017 |
// DisconnectFromNetwork disconnects container from network n. |
| 1018 | 1018 |
func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error {
|
| 1019 |
- n, err := daemon.FindNetwork(networkName) |
|
| 1019 |
+ n, err := daemon.FindUniqueNetwork(networkName) |
|
| 1020 | 1020 |
container.Lock() |
| 1021 | 1021 |
defer container.Unlock() |
| 1022 | 1022 |
|
| ... | ... |
@@ -1087,3 +1087,12 @@ func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) er |
| 1087 | 1087 |
} |
| 1088 | 1088 |
return sb.DisableService() |
| 1089 | 1089 |
} |
| 1090 |
+ |
|
| 1091 |
+func getNetworkID(name string, endpointSettings *networktypes.EndpointSettings) string {
|
|
| 1092 |
+ // We only want to prefer NetworkID for user defined networks. |
|
| 1093 |
+ // For systems like bridge, none, etc. the name is preferred (otherwise restart may cause issues) |
|
| 1094 |
+ if containertypes.NetworkMode(name).IsUserDefined() && endpointSettings != nil && endpointSettings.NetworkID != "" {
|
|
| 1095 |
+ return endpointSettings.NetworkID |
|
| 1096 |
+ } |
|
| 1097 |
+ return name |
|
| 1098 |
+} |
| ... | ... |
@@ -170,7 +170,7 @@ func (daemon *Daemon) initializeNetworkingPaths(container *container.Container, |
| 170 | 170 |
|
| 171 | 171 |
if nc.NetworkSettings != nil {
|
| 172 | 172 |
for n := range nc.NetworkSettings.Networks {
|
| 173 |
- sn, err := daemon.FindNetwork(n) |
|
| 173 |
+ sn, err := daemon.FindUniqueNetwork(n) |
|
| 174 | 174 |
if err != nil {
|
| 175 | 175 |
continue |
| 176 | 176 |
} |
| ... | ... |
@@ -317,7 +317,7 @@ func TestValidateContainerIsolation(t *testing.T) {
|
| 317 | 317 |
|
| 318 | 318 |
func TestFindNetworkErrorType(t *testing.T) {
|
| 319 | 319 |
d := Daemon{}
|
| 320 |
- _, err := d.FindNetwork("fakeNet")
|
|
| 320 |
+ _, err := d.FindUniqueNetwork("fakeNet")
|
|
| 321 | 321 |
_, ok := errors.Cause(err).(libnetwork.ErrNoSuchNetwork) |
| 322 | 322 |
if !errdefs.IsNotFound(err) || !ok {
|
| 323 | 323 |
assert.Fail(t, "The FindNetwork method MUST always return an error that implements the NotFound interface and is ErrNoSuchNetwork") |
| ... | ... |
@@ -226,20 +226,3 @@ func translateContainerdStartErr(cmd string, setExitCode func(int), err error) e |
| 226 | 226 |
// TODO: it would be nice to get some better errors from containerd so we can return better errors here |
| 227 | 227 |
return retErr |
| 228 | 228 |
} |
| 229 |
- |
|
| 230 |
-// TODO: cpuguy83 take care of it once the new library is ready |
|
| 231 |
-type errNotFound struct{ error }
|
|
| 232 |
- |
|
| 233 |
-func (errNotFound) NotFound() {}
|
|
| 234 |
- |
|
| 235 |
-func (e errNotFound) Cause() error {
|
|
| 236 |
- return e.error |
|
| 237 |
-} |
|
| 238 |
- |
|
| 239 |
-// notFound is a helper to create an error of the class with the same name from any error type |
|
| 240 |
-func notFound(err error) error {
|
|
| 241 |
- if err == nil {
|
|
| 242 |
- return nil |
|
| 243 |
- } |
|
| 244 |
- return errNotFound{err}
|
|
| 245 |
-} |
| ... | ... |
@@ -29,36 +29,36 @@ func (daemon *Daemon) NetworkControllerEnabled() bool {
|
| 29 | 29 |
return daemon.netController != nil |
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 |
-// FindNetwork function finds a network for a given string that can represent network name or id |
|
| 33 |
-func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) {
|
|
| 34 |
- // 1. match by full ID. |
|
| 35 |
- n, err := daemon.GetNetworkByID(idName) |
|
| 36 |
- if err == nil || !isNoSuchNetworkError(err) {
|
|
| 37 |
- return n, err |
|
| 38 |
- } |
|
| 39 |
- |
|
| 40 |
- // 2. match by full name |
|
| 41 |
- n, err = daemon.GetNetworkByName(idName) |
|
| 42 |
- if err == nil || !isNoSuchNetworkError(err) {
|
|
| 43 |
- return n, err |
|
| 44 |
- } |
|
| 45 |
- |
|
| 46 |
- // 3. match by ID prefix |
|
| 47 |
- list := daemon.GetNetworksByIDPrefix(idName) |
|
| 48 |
- if len(list) == 0 {
|
|
| 49 |
- // Be very careful to change the error type here, the libnetwork.ErrNoSuchNetwork error is used by the controller |
|
| 50 |
- // to retry the creation of the network as managed through the swarm manager |
|
| 51 |
- return nil, errors.WithStack(notFound(libnetwork.ErrNoSuchNetwork(idName))) |
|
| 52 |
- } |
|
| 53 |
- if len(list) > 1 {
|
|
| 54 |
- return nil, errors.WithStack(invalidIdentifier(idName)) |
|
| 32 |
+// FindUniqueNetwork returns a network based on: |
|
| 33 |
+// 1. Full ID |
|
| 34 |
+// 2. Full Name |
|
| 35 |
+// 3. Partial ID |
|
| 36 |
+// as long as there is no ambiguity |
|
| 37 |
+func (daemon *Daemon) FindUniqueNetwork(term string) (libnetwork.Network, error) {
|
|
| 38 |
+ listByFullName := []libnetwork.Network{}
|
|
| 39 |
+ listByPartialID := []libnetwork.Network{}
|
|
| 40 |
+ for _, nw := range daemon.GetNetworks() {
|
|
| 41 |
+ if nw.ID() == term {
|
|
| 42 |
+ return nw, nil |
|
| 43 |
+ } |
|
| 44 |
+ if nw.Name() == term {
|
|
| 45 |
+ listByFullName = append(listByFullName, nw) |
|
| 46 |
+ } |
|
| 47 |
+ if strings.HasPrefix(nw.ID(), term) {
|
|
| 48 |
+ listByPartialID = append(listByPartialID, nw) |
|
| 49 |
+ } |
|
| 55 | 50 |
} |
| 56 |
- return list[0], nil |
|
| 57 |
-} |
|
| 58 |
- |
|
| 59 |
-func isNoSuchNetworkError(err error) bool {
|
|
| 60 |
- _, ok := err.(libnetwork.ErrNoSuchNetwork) |
|
| 61 |
- return ok |
|
| 51 |
+ switch {
|
|
| 52 |
+ case len(listByFullName) == 1: |
|
| 53 |
+ return listByFullName[0], nil |
|
| 54 |
+ case len(listByFullName) > 1: |
|
| 55 |
+ return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
|
|
| 56 |
+ case len(listByPartialID) == 1: |
|
| 57 |
+ return listByPartialID[0], nil |
|
| 58 |
+ case len(listByPartialID) > 1: |
|
| 59 |
+ return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
|
|
| 60 |
+ } |
|
| 61 |
+ return nil, libnetwork.ErrNoSuchNetwork(term) |
|
| 62 | 62 |
} |
| 63 | 63 |
|
| 64 | 64 |
// GetNetworkByID function returns a network whose ID matches the given ID. |
| ... | ... |
@@ -104,7 +104,11 @@ func (daemon *Daemon) GetNetworksByIDPrefix(partialID string) []libnetwork.Netwo |
| 104 | 104 |
|
| 105 | 105 |
// getAllNetworks returns a list containing all networks |
| 106 | 106 |
func (daemon *Daemon) getAllNetworks() []libnetwork.Network {
|
| 107 |
- return daemon.netController.Networks() |
|
| 107 |
+ c := daemon.netController |
|
| 108 |
+ if c == nil {
|
|
| 109 |
+ return nil |
|
| 110 |
+ } |
|
| 111 |
+ return c.Networks() |
|
| 108 | 112 |
} |
| 109 | 113 |
|
| 110 | 114 |
type ingressJob struct {
|
| ... | ... |
@@ -274,7 +278,9 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string |
| 274 | 274 |
// check if user defined CheckDuplicate, if set true, return err |
| 275 | 275 |
// otherwise prepare a warning message |
| 276 | 276 |
if create.CheckDuplicate {
|
| 277 |
- return nil, libnetwork.NetworkNameError(create.Name) |
|
| 277 |
+ if !agent || nw.Info().Dynamic() {
|
|
| 278 |
+ return nil, libnetwork.NetworkNameError(create.Name) |
|
| 279 |
+ } |
|
| 278 | 280 |
} |
| 279 | 281 |
warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
|
| 280 | 282 |
} |
| ... | ... |
@@ -464,25 +470,56 @@ func (daemon *Daemon) GetNetworkDriverList() []string {
|
| 464 | 464 |
} |
| 465 | 465 |
|
| 466 | 466 |
// DeleteManagedNetwork deletes an agent network. |
| 467 |
+// The requirement of networkID is enforced. |
|
| 467 | 468 |
func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
|
| 468 |
- return daemon.deleteNetwork(networkID, true) |
|
| 469 |
+ n, err := daemon.GetNetworkByID(networkID) |
|
| 470 |
+ if err != nil {
|
|
| 471 |
+ return err |
|
| 472 |
+ } |
|
| 473 |
+ return daemon.deleteNetwork(n, true) |
|
| 469 | 474 |
} |
| 470 | 475 |
|
| 471 | 476 |
// DeleteNetwork destroys a network unless it's one of docker's predefined networks. |
| 472 | 477 |
func (daemon *Daemon) DeleteNetwork(networkID string) error {
|
| 473 |
- return daemon.deleteNetwork(networkID, false) |
|
| 474 |
-} |
|
| 475 |
- |
|
| 476 |
-func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
|
|
| 477 |
- nw, err := daemon.FindNetwork(networkID) |
|
| 478 |
+ n, err := daemon.GetNetworkByID(networkID) |
|
| 478 | 479 |
if err != nil {
|
| 479 | 480 |
return err |
| 480 | 481 |
} |
| 482 |
+ return daemon.deleteNetwork(n, false) |
|
| 483 |
+} |
|
| 481 | 484 |
|
| 482 |
- if nw.Info().Ingress() {
|
|
| 483 |
- return nil |
|
| 485 |
+func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) {
|
|
| 486 |
+ controller := daemon.netController |
|
| 487 |
+ |
|
| 488 |
+ //The only endpoint left should be the LB endpoint (nw.Name() + "-endpoint") |
|
| 489 |
+ endpoints := n.Endpoints() |
|
| 490 |
+ if len(endpoints) == 1 {
|
|
| 491 |
+ sandboxName := n.Name() + "-sbox" |
|
| 492 |
+ |
|
| 493 |
+ info := endpoints[0].Info() |
|
| 494 |
+ if info != nil {
|
|
| 495 |
+ sb := info.Sandbox() |
|
| 496 |
+ if sb != nil {
|
|
| 497 |
+ if err := sb.DisableService(); err != nil {
|
|
| 498 |
+ logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
|
|
| 499 |
+ //Ignore error and attempt to delete the load balancer endpoint |
|
| 500 |
+ } |
|
| 501 |
+ } |
|
| 502 |
+ } |
|
| 503 |
+ |
|
| 504 |
+ if err := endpoints[0].Delete(true); err != nil {
|
|
| 505 |
+ logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoints[0].Name(), endpoints[0].ID(), sandboxName, err)
|
|
| 506 |
+ //Ignore error and attempt to delete the sandbox. |
|
| 507 |
+ } |
|
| 508 |
+ |
|
| 509 |
+ if err := controller.SandboxDestroy(sandboxName); err != nil {
|
|
| 510 |
+ logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err)
|
|
| 511 |
+ //Ignore error and attempt to delete the network. |
|
| 512 |
+ } |
|
| 484 | 513 |
} |
| 514 |
+} |
|
| 485 | 515 |
|
| 516 |
+func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
|
|
| 486 | 517 |
if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
|
| 487 | 518 |
err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
|
| 488 | 519 |
return notAllowedError{err}
|
| ... | ... |
@@ -159,7 +159,7 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
|
| 159 | 159 |
gwHNSID := "" |
| 160 | 160 |
if c.NetworkSettings != nil {
|
| 161 | 161 |
for n := range c.NetworkSettings.Networks {
|
| 162 |
- sn, err := daemon.FindNetwork(n) |
|
| 162 |
+ sn, err := daemon.FindUniqueNetwork(n) |
|
| 163 | 163 |
if err != nil {
|
| 164 | 164 |
continue |
| 165 | 165 |
} |
| ... | ... |
@@ -11,6 +11,7 @@ import ( |
| 11 | 11 |
"github.com/docker/docker/client" |
| 12 | 12 |
"github.com/docker/docker/integration-cli/request" |
| 13 | 13 |
"github.com/gotestyourself/gotestyourself/poll" |
| 14 |
+ "github.com/stretchr/testify/assert" |
|
| 14 | 15 |
"github.com/stretchr/testify/require" |
| 15 | 16 |
"golang.org/x/net/context" |
| 16 | 17 |
) |
| ... | ... |
@@ -80,6 +81,68 @@ func TestCreateServiceMultipleTimes(t *testing.T) {
|
| 80 | 80 |
poll.WaitOn(t, networkIsRemoved(client, overlayID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) |
| 81 | 81 |
} |
| 82 | 82 |
|
| 83 |
+func TestCreateWithDuplicateNetworkNames(t *testing.T) {
|
|
| 84 |
+ defer setupTest(t)() |
|
| 85 |
+ d := newSwarm(t) |
|
| 86 |
+ defer d.Stop(t) |
|
| 87 |
+ client, err := request.NewClientForHost(d.Sock()) |
|
| 88 |
+ require.NoError(t, err) |
|
| 89 |
+ |
|
| 90 |
+ name := "foo" |
|
| 91 |
+ networkCreate := types.NetworkCreate{
|
|
| 92 |
+ CheckDuplicate: false, |
|
| 93 |
+ Driver: "bridge", |
|
| 94 |
+ } |
|
| 95 |
+ |
|
| 96 |
+ n1, err := client.NetworkCreate(context.Background(), name, networkCreate) |
|
| 97 |
+ require.NoError(t, err) |
|
| 98 |
+ |
|
| 99 |
+ n2, err := client.NetworkCreate(context.Background(), name, networkCreate) |
|
| 100 |
+ require.NoError(t, err) |
|
| 101 |
+ |
|
| 102 |
+ // Dupliates with name but with different driver |
|
| 103 |
+ networkCreate.Driver = "overlay" |
|
| 104 |
+ n3, err := client.NetworkCreate(context.Background(), name, networkCreate) |
|
| 105 |
+ require.NoError(t, err) |
|
| 106 |
+ |
|
| 107 |
+ // Create Service with the same name |
|
| 108 |
+ var instances uint64 = 1 |
|
| 109 |
+ serviceSpec := swarmServiceSpec("top", instances)
|
|
| 110 |
+ |
|
| 111 |
+ serviceSpec.TaskTemplate.Networks = append(serviceSpec.TaskTemplate.Networks, swarm.NetworkAttachmentConfig{Target: name})
|
|
| 112 |
+ |
|
| 113 |
+ service, err := client.ServiceCreate(context.Background(), serviceSpec, types.ServiceCreateOptions{})
|
|
| 114 |
+ require.NoError(t, err) |
|
| 115 |
+ |
|
| 116 |
+ poll.WaitOn(t, serviceRunningTasksCount(client, service.ID, instances)) |
|
| 117 |
+ |
|
| 118 |
+ resp, _, err := client.ServiceInspectWithRaw(context.Background(), service.ID, types.ServiceInspectOptions{})
|
|
| 119 |
+ require.NoError(t, err) |
|
| 120 |
+ assert.Equal(t, n3.ID, resp.Spec.TaskTemplate.Networks[0].Target) |
|
| 121 |
+ |
|
| 122 |
+ // Remove Service |
|
| 123 |
+ err = client.ServiceRemove(context.Background(), service.ID) |
|
| 124 |
+ require.NoError(t, err) |
|
| 125 |
+ |
|
| 126 |
+ // Make sure task has been destroyed. |
|
| 127 |
+ poll.WaitOn(t, serviceIsRemoved(client, service.ID)) |
|
| 128 |
+ |
|
| 129 |
+ // Remove networks |
|
| 130 |
+ err = client.NetworkRemove(context.Background(), n3.ID) |
|
| 131 |
+ require.NoError(t, err) |
|
| 132 |
+ |
|
| 133 |
+ err = client.NetworkRemove(context.Background(), n2.ID) |
|
| 134 |
+ require.NoError(t, err) |
|
| 135 |
+ |
|
| 136 |
+ err = client.NetworkRemove(context.Background(), n1.ID) |
|
| 137 |
+ require.NoError(t, err) |
|
| 138 |
+ |
|
| 139 |
+ // Make sure networks have been destroyed. |
|
| 140 |
+ poll.WaitOn(t, networkIsRemoved(client, n3.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) |
|
| 141 |
+ poll.WaitOn(t, networkIsRemoved(client, n2.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) |
|
| 142 |
+ poll.WaitOn(t, networkIsRemoved(client, n1.ID), poll.WithTimeout(1*time.Minute), poll.WithDelay(10*time.Second)) |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 83 | 145 |
func swarmServiceSpec(name string, replicas uint64) swarm.ServiceSpec {
|
| 84 | 146 |
return swarm.ServiceSpec{
|
| 85 | 147 |
Annotations: swarm.Annotations{
|