1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

Update FindUniqueNetwork to address network name duplications

This fix is part of the effort to address 30242 where
issue arise because of the fact that multiple networks
may share the same name (within or across local/swarm scopes).

The focus of this fix is to allow creation of service
when a network in local scope has the same name as the
service network.

An integration test has been added.

This fix fixes 30242.

Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
This commit is contained in:
Yong Tang 2017-10-31 19:46:53 +00:00
parent 39faf2a3a8
commit cafed80cd0
9 changed files with 270 additions and 77 deletions

View file

@ -12,11 +12,11 @@ import (
// Backend is all the methods that need to be implemented // Backend is all the methods that need to be implemented
// to provide network specific functionality. // to provide network specific functionality.
type Backend interface { type Backend interface {
FindNetwork(idName string) (libnetwork.Network, error) FindUniqueNetwork(idName string) (libnetwork.Network, error)
GetNetworks() []libnetwork.Network GetNetworks() []libnetwork.Network
CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) CreateNetwork(nc types.NetworkCreateRequest) (*types.NetworkCreateResponse, error)
ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error
DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error
DeleteNetwork(name string) error DeleteNetwork(networkID string) error
NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error) NetworksPrune(ctx context.Context, pruneFilters filters.Args) (*types.NetworksPruneReport, error)
} }

View file

@ -2,6 +2,7 @@ package network
import ( import (
"encoding/json" "encoding/json"
"fmt"
"net/http" "net/http"
"strconv" "strconv"
"strings" "strings"
@ -288,7 +289,12 @@ func (n *networkRouter) postNetworkConnect(ctx context.Context, w http.ResponseW
return err return err
} }
return n.backend.ConnectContainerToNetwork(connect.Container, vars["id"], connect.EndpointConfig) // Always make sure there is no ambiguity with respect to the network ID/name
nw, err := n.backend.FindUniqueNetwork(vars["id"])
if err != nil {
return err
}
return n.backend.ConnectContainerToNetwork(connect.Container, nw.ID(), connect.EndpointConfig)
} }
func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { func (n *networkRouter) postNetworkDisconnect(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
@ -312,16 +318,20 @@ func (n *networkRouter) deleteNetwork(ctx context.Context, w http.ResponseWriter
if err := httputils.ParseForm(r); err != nil { if err := httputils.ParseForm(r); err != nil {
return err return err
} }
if _, err := n.cluster.GetNetwork(vars["id"]); err == nil {
if err = n.cluster.RemoveNetwork(vars["id"]); err != nil { nw, err := n.findUniqueNetwork(vars["id"])
if err != nil {
return err return err
} }
w.WriteHeader(http.StatusNoContent) if nw.Scope == "swarm" {
return nil if err = n.cluster.RemoveNetwork(nw.ID); err != nil {
}
if err := n.backend.DeleteNetwork(vars["id"]); err != nil {
return err return err
} }
} else {
if err := n.backend.DeleteNetwork(nw.ID); err != nil {
return err
}
}
w.WriteHeader(http.StatusNoContent) w.WriteHeader(http.StatusNoContent)
return nil return nil
} }
@ -518,3 +528,79 @@ func (n *networkRouter) postNetworksPrune(ctx context.Context, w http.ResponseWr
} }
return httputils.WriteJSON(w, http.StatusOK, pruneReport) return httputils.WriteJSON(w, http.StatusOK, pruneReport)
} }
// findUniqueNetwork will search network across different scopes (both local and swarm).
// NOTE: This findUniqueNetwork is differnt from FindUniqueNetwork from the daemon.
// In case multiple networks have duplicate names, return error.
// First find based on full ID, return immediately once one is found.
// If a network appears both in swarm and local, assume it is in local first
// For full name and partial ID, save the result first, and process later
// in case multiple records was found based on the same term
// TODO (yongtang): should we wrap with version here for backward compatibility?
func (n *networkRouter) findUniqueNetwork(term string) (types.NetworkResource, error) {
listByFullName := map[string]types.NetworkResource{}
listByPartialID := map[string]types.NetworkResource{}
nw := n.backend.GetNetworks()
for _, network := range nw {
if network.ID() == term {
return *n.buildDetailedNetworkResources(network, false), nil
}
if network.Name() == term {
// No need to check the ID collision here as we are still in
// local scope and the network ID is unique in this scope.
listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, false)
}
if strings.HasPrefix(network.ID(), term) {
// No need to check the ID collision here as we are still in
// local scope and the network ID is unique in this scope.
listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, false)
}
}
nr, _ := n.cluster.GetNetworks()
for _, network := range nr {
if network.ID == term {
return network, nil
}
if network.Name == term {
// Check the ID collision as we are in swarm scope here, and
// the map (of the listByFullName) may have already had a
// network with the same ID (from local scope previously)
if _, ok := listByFullName[network.ID]; !ok {
listByFullName[network.ID] = network
}
}
if strings.HasPrefix(network.ID, term) {
// Check the ID collision as we are in swarm scope here, and
// the map (of the listByPartialID) may have already had a
// network with the same ID (from local scope previously)
if _, ok := listByPartialID[network.ID]; !ok {
listByPartialID[network.ID] = network
}
}
}
// Find based on full name, returns true only if no duplicates
if len(listByFullName) == 1 {
for _, v := range listByFullName {
return v, nil
}
}
if len(listByFullName) > 1 {
return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
}
// Find based on partial ID, returns true only if no duplicates
if len(listByPartialID) == 1 {
for _, v := range listByPartialID {
return v, nil
}
}
if len(listByPartialID) > 1 {
return types.NetworkResource{}, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
}
return types.NetworkResource{}, libnetwork.ErrNoSuchNetwork(term)
}

View file

@ -27,8 +27,8 @@ import (
// Backend defines the executor component for a swarm agent. // Backend defines the executor component for a swarm agent.
type Backend interface { type Backend interface {
CreateManagedNetwork(clustertypes.NetworkCreateRequest) error CreateManagedNetwork(clustertypes.NetworkCreateRequest) error
DeleteManagedNetwork(name string) error DeleteManagedNetwork(networkID string) error
FindNetwork(idName string) (libnetwork.Network, error) FindUniqueNetwork(idName string) (libnetwork.Network, error)
SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error) SetupIngress(clustertypes.NetworkCreateRequest, string) (<-chan struct{}, error)
ReleaseIngress() (<-chan struct{}, error) ReleaseIngress() (<-chan struct{}, error)
PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error PullImage(ctx context.Context, image, tag, platform string, metaHeaders map[string][]string, authConfig *types.AuthConfig, outStream io.Writer) error

View file

@ -143,8 +143,8 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
} }
func (c *containerAdapter) createNetworks(ctx context.Context) error { func (c *containerAdapter) createNetworks(ctx context.Context) error {
for _, network := range c.container.networks() { for name := range c.container.networksAttachments {
ncr, err := c.container.networkCreateRequest(network) ncr, err := c.container.networkCreateRequest(name)
if err != nil { if err != nil {
return err return err
} }
@ -162,15 +162,15 @@ func (c *containerAdapter) createNetworks(ctx context.Context) error {
} }
func (c *containerAdapter) removeNetworks(ctx context.Context) error { func (c *containerAdapter) removeNetworks(ctx context.Context) error {
for _, nid := range c.container.networks() { for name, v := range c.container.networksAttachments {
if err := c.backend.DeleteManagedNetwork(nid); err != nil { if err := c.backend.DeleteManagedNetwork(v.Network.ID); err != nil {
switch err.(type) { switch err.(type) {
case *libnetwork.ActiveEndpointsError: case *libnetwork.ActiveEndpointsError:
continue continue
case libnetwork.ErrNoSuchNetwork: case libnetwork.ErrNoSuchNetwork:
continue continue
default: default:
log.G(ctx).Errorf("network %s remove failed: %v", nid, err) log.G(ctx).Errorf("network %s remove failed: %v", name, err)
return err return err
} }
} }

View file

@ -507,7 +507,7 @@ func getEndpointConfig(na *api.NetworkAttachment, b executorpkg.Backend) *networ
DriverOpts: na.DriverAttachmentOpts, DriverOpts: na.DriverAttachmentOpts,
} }
if v, ok := na.Network.Spec.Annotations.Labels["com.docker.swarm.predefined"]; ok && v == "true" { if v, ok := na.Network.Spec.Annotations.Labels["com.docker.swarm.predefined"]; ok && v == "true" {
if ln, err := b.FindNetwork(na.Network.Spec.Annotations.Name); err == nil { if ln, err := b.FindUniqueNetwork(na.Network.Spec.Annotations.Name); err == nil {
n.NetworkID = ln.ID() n.NetworkID = ln.ID()
} }
} }
@ -575,19 +575,6 @@ func (c *containerConfig) serviceConfig() *clustertypes.ServiceConfig {
return svcCfg return svcCfg
} }
// networks returns a list of network names attached to the container. The
// returned name can be used to lookup the corresponding network create
// options.
func (c *containerConfig) networks() []string {
var networks []string
for name := range c.networksAttachments {
networks = append(networks, name)
}
return networks
}
func (c *containerConfig) networkCreateRequest(name string) (clustertypes.NetworkCreateRequest, error) { func (c *containerConfig) networkCreateRequest(name string) (clustertypes.NetworkCreateRequest, error) {
na, ok := c.networksAttachments[name] na, ok := c.networksAttachments[name]
if !ok { if !ok {

View file

@ -292,7 +292,7 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control
for i, n := range networks { for i, n := range networks {
apiNetwork, err := getNetwork(ctx, client, n.Target) apiNetwork, err := getNetwork(ctx, client, n.Target)
if err != nil { if err != nil {
ln, _ := c.config.Backend.FindNetwork(n.Target) ln, _ := c.config.Backend.FindUniqueNetwork(n.Target)
if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) { if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) {
// Need to retrieve the corresponding predefined swarm network // Need to retrieve the corresponding predefined swarm network
// and use its id for the request. // and use its id for the request.

View file

@ -251,8 +251,8 @@ func (daemon *Daemon) updateNetworkSettings(container *container.Container, n li
return runconfig.ErrConflictHostNetwork return runconfig.ErrConflictHostNetwork
} }
for s := range container.NetworkSettings.Networks { for s, v := range container.NetworkSettings.Networks {
sn, err := daemon.FindNetwork(s) sn, err := daemon.FindUniqueNetwork(getNetworkID(s, v.EndpointSettings))
if err != nil { if err != nil {
continue continue
} }
@ -308,8 +308,8 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
// Find if container is connected to the default bridge network // Find if container is connected to the default bridge network
var n libnetwork.Network var n libnetwork.Network
for name := range container.NetworkSettings.Networks { for name, v := range container.NetworkSettings.Networks {
sn, err := daemon.FindNetwork(name) sn, err := daemon.FindUniqueNetwork(getNetworkID(name, v.EndpointSettings))
if err != nil { if err != nil {
continue continue
} }
@ -339,7 +339,7 @@ func (daemon *Daemon) updateNetwork(container *container.Container) error {
} }
func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrName string, epConfig *networktypes.EndpointSettings) (libnetwork.Network, *networktypes.NetworkingConfig, error) { func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrName string, epConfig *networktypes.EndpointSettings) (libnetwork.Network, *networktypes.NetworkingConfig, error) {
n, err := daemon.FindNetwork(idOrName) n, err := daemon.FindUniqueNetwork(getNetworkID(idOrName, epConfig))
if err != nil { if err != nil {
// We should always be able to find the network for a // We should always be able to find the network for a
// managed container. // managed container.
@ -377,16 +377,16 @@ func (daemon *Daemon) findAndAttachNetwork(container *container.Container, idOrN
// trigger attachment in the swarm cluster manager. // trigger attachment in the swarm cluster manager.
if daemon.clusterProvider != nil { if daemon.clusterProvider != nil {
var err error var err error
config, err = daemon.clusterProvider.AttachNetwork(idOrName, container.ID, addresses) config, err = daemon.clusterProvider.AttachNetwork(getNetworkID(idOrName, epConfig), container.ID, addresses)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
} }
n, err = daemon.FindNetwork(idOrName) n, err = daemon.FindUniqueNetwork(getNetworkID(idOrName, epConfig))
if err != nil { if err != nil {
if daemon.clusterProvider != nil { if daemon.clusterProvider != nil {
if err := daemon.clusterProvider.DetachNetwork(idOrName, container.ID); err != nil { if err := daemon.clusterProvider.DetachNetwork(getNetworkID(idOrName, epConfig), container.ID); err != nil {
logrus.Warnf("Could not rollback attachment for container %s to network %s: %v", container.ID, idOrName, err) logrus.Warnf("Could not rollback attachment for container %s to network %s: %v", container.ID, idOrName, err)
} }
} }
@ -437,7 +437,7 @@ func (daemon *Daemon) updateContainerNetworkSettings(container *container.Contai
if mode.IsUserDefined() { if mode.IsUserDefined() {
var err error var err error
n, err = daemon.FindNetwork(networkName) n, err = daemon.FindUniqueNetwork(networkName)
if err == nil { if err == nil {
networkName = n.Name() networkName = n.Name()
} }
@ -797,7 +797,7 @@ func (daemon *Daemon) connectToNetwork(container *container.Container, idOrName
// ForceEndpointDelete deletes an endpoint from a network forcefully // ForceEndpointDelete deletes an endpoint from a network forcefully
func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error { func (daemon *Daemon) ForceEndpointDelete(name string, networkName string) error {
n, err := daemon.FindNetwork(networkName) n, err := daemon.FindUniqueNetwork(networkName)
if err != nil { if err != nil {
return err return err
} }
@ -949,7 +949,7 @@ func (daemon *Daemon) releaseNetwork(container *container.Container) {
var networks []libnetwork.Network var networks []libnetwork.Network
for n, epSettings := range settings { for n, epSettings := range settings {
if nw, err := daemon.FindNetwork(n); err == nil { if nw, err := daemon.FindUniqueNetwork(getNetworkID(n, epSettings.EndpointSettings)); err == nil {
networks = append(networks, nw) networks = append(networks, nw)
} }
@ -993,7 +993,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
return errRemovalContainer(container.ID) return errRemovalContainer(container.ID)
} }
n, err := daemon.FindNetwork(idOrName) n, err := daemon.FindUniqueNetwork(idOrName)
if err == nil && n != nil { if err == nil && n != nil {
if err := daemon.updateNetworkConfig(container, n, endpointConfig, true); err != nil { if err := daemon.updateNetworkConfig(container, n, endpointConfig, true); err != nil {
return err return err
@ -1016,7 +1016,7 @@ func (daemon *Daemon) ConnectToNetwork(container *container.Container, idOrName
// DisconnectFromNetwork disconnects container from network n. // DisconnectFromNetwork disconnects container from network n.
func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error { func (daemon *Daemon) DisconnectFromNetwork(container *container.Container, networkName string, force bool) error {
n, err := daemon.FindNetwork(networkName) n, err := daemon.FindUniqueNetwork(networkName)
container.Lock() container.Lock()
defer container.Unlock() defer container.Unlock()
@ -1087,3 +1087,12 @@ func (daemon *Daemon) DeactivateContainerServiceBinding(containerName string) er
} }
return sb.DisableService() return sb.DisableService()
} }
func getNetworkID(name string, endpointSettings *networktypes.EndpointSettings) string {
// We only want to prefer NetworkID for user defined networks.
// For systems like bridge, none, etc. the name is preferred (otherwise restart may cause issues)
if containertypes.NetworkMode(name).IsUserDefined() && endpointSettings != nil && endpointSettings.NetworkID != "" {
return endpointSettings.NetworkID
}
return name
}

View file

@ -29,31 +29,36 @@ func (daemon *Daemon) NetworkControllerEnabled() bool {
return daemon.netController != nil return daemon.netController != nil
} }
// FindNetwork function finds a network for a given string that can represent network name or id // FindUniqueNetwork returns a network based on:
func (daemon *Daemon) FindNetwork(idName string) (libnetwork.Network, error) { // 1. Full ID
// 1. match by full ID. // 2. Full Name
n, err := daemon.GetNetworkByID(idName) // 3. Partial ID
if err == nil || !isNoSuchNetworkError(err) { // as long as there is no ambiguity
return n, err func (daemon *Daemon) FindUniqueNetwork(term string) (libnetwork.Network, error) {
listByFullName := []libnetwork.Network{}
listByPartialID := []libnetwork.Network{}
for _, nw := range daemon.GetNetworks() {
if nw.ID() == term {
return nw, nil
} }
if nw.Name() == term {
// 2. match by full name listByFullName = append(listByFullName, nw)
n, err = daemon.GetNetworkByName(idName)
if err == nil || !isNoSuchNetworkError(err) {
return n, err
} }
if strings.HasPrefix(nw.ID(), term) {
// 3. match by ID prefix listByPartialID = append(listByPartialID, nw)
list := daemon.GetNetworksByIDPrefix(idName)
if len(list) == 0 {
// Be very careful to change the error type here, the libnetwork.ErrNoSuchNetwork error is used by the controller
// to retry the creation of the network as managed through the swarm manager
return nil, errors.WithStack(notFound(libnetwork.ErrNoSuchNetwork(idName)))
} }
if len(list) > 1 {
return nil, errors.WithStack(invalidIdentifier(idName))
} }
return list[0], nil switch {
case len(listByFullName) == 1:
return listByFullName[0], nil
case len(listByFullName) > 1:
return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on name)", term, len(listByFullName))
case len(listByPartialID) == 1:
return listByPartialID[0], nil
case len(listByPartialID) > 1:
return nil, fmt.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))
}
return nil, libnetwork.ErrNoSuchNetwork(term)
} }
func isNoSuchNetworkError(err error) bool { func isNoSuchNetworkError(err error) bool {
@ -274,8 +279,10 @@ func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string
// check if user defined CheckDuplicate, if set true, return err // check if user defined CheckDuplicate, if set true, return err
// otherwise prepare a warning message // otherwise prepare a warning message
if create.CheckDuplicate { if create.CheckDuplicate {
if !agent || nw.Info().Dynamic() {
return nil, libnetwork.NetworkNameError(create.Name) return nil, libnetwork.NetworkNameError(create.Name)
} }
}
warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID()) warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
} }
@ -464,25 +471,56 @@ func (daemon *Daemon) GetNetworkDriverList() []string {
} }
// DeleteManagedNetwork deletes an agent network. // DeleteManagedNetwork deletes an agent network.
// The requirement of networkID is enforced.
func (daemon *Daemon) DeleteManagedNetwork(networkID string) error { func (daemon *Daemon) DeleteManagedNetwork(networkID string) error {
return daemon.deleteNetwork(networkID, true) n, err := daemon.GetNetworkByID(networkID)
if err != nil {
return err
}
return daemon.deleteNetwork(n, true)
} }
// DeleteNetwork destroys a network unless it's one of docker's predefined networks. // DeleteNetwork destroys a network unless it's one of docker's predefined networks.
func (daemon *Daemon) DeleteNetwork(networkID string) error { func (daemon *Daemon) DeleteNetwork(networkID string) error {
return daemon.deleteNetwork(networkID, false) n, err := daemon.GetNetworkByID(networkID)
}
func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error {
nw, err := daemon.FindNetwork(networkID)
if err != nil { if err != nil {
return err return err
} }
return daemon.deleteNetwork(n, false)
}
if nw.Info().Ingress() { func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) {
return nil controller := daemon.netController
//The only endpoint left should be the LB endpoint (nw.Name() + "-endpoint")
endpoints := n.Endpoints()
if len(endpoints) == 1 {
sandboxName := n.Name() + "-sbox"
info := endpoints[0].Info()
if info != nil {
sb := info.Sandbox()
if sb != nil {
if err := sb.DisableService(); err != nil {
logrus.Warnf("Failed to disable service on sandbox %s: %v", sandboxName, err)
//Ignore error and attempt to delete the load balancer endpoint
}
}
} }
if err := endpoints[0].Delete(true); err != nil {
logrus.Warnf("Failed to delete endpoint %s (%s) in %s: %v", endpoints[0].Name(), endpoints[0].ID(), sandboxName, err)
//Ignore error and attempt to delete the sandbox.
}
if err := controller.SandboxDestroy(sandboxName); err != nil {
logrus.Warnf("Failed to delete %s sandbox: %v", sandboxName, err)
//Ignore error and attempt to delete the network.
}
}
}
func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error {
if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic { if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic {
err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name()) err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name())
return notAllowedError{err} return notAllowedError{err}

View file

@ -2137,3 +2137,76 @@ func (s *DockerSwarmSuite) TestSwarmClusterEventsConfig(c *check.C) {
// filtered by config // filtered by config
waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount) waitForEvent(c, d, t1, "-f type=config", "config remove "+id, defaultRetryCount)
} }
func (s *DockerSwarmSuite) TestServiceCreateWithDuplicateNetworkNames(c *check.C) {
d := s.AddDaemon(c, true, true)
name := "foo"
networkCreateRequest := types.NetworkCreateRequest{
Name: name,
NetworkCreate: types.NetworkCreate{
CheckDuplicate: false,
Driver: "bridge",
},
}
// Create networks with the same name, 2 in local scope and 1 in swarm scope
var n1 types.NetworkCreateResponse
status, body, err := d.SockRequest("POST", "/networks/create", networkCreateRequest)
c.Assert(err, checker.IsNil, check.Commentf(string(body)))
c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
c.Assert(json.Unmarshal(body, &n1), checker.IsNil)
var n2 types.NetworkCreateResponse
status, body, err = d.SockRequest("POST", "/networks/create", networkCreateRequest)
c.Assert(err, checker.IsNil, check.Commentf(string(body)))
c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
c.Assert(json.Unmarshal(body, &n2), checker.IsNil)
var n3 types.NetworkCreateResponse
// Dupliates with name but with different driver
networkCreateRequest.NetworkCreate.Driver = "overlay"
status, body, err = d.SockRequest("POST", "/networks/create", networkCreateRequest)
c.Assert(err, checker.IsNil, check.Commentf(string(body)))
c.Assert(status, checker.Equals, http.StatusCreated, check.Commentf(string(body)))
c.Assert(json.Unmarshal(body, &n3), checker.IsNil)
// Create Service with the same name
d.CreateService(c, simpleTestService, func(s *swarm.Service) {
s.Spec.Name = "top"
s.Spec.TaskTemplate.Networks = []swarm.NetworkAttachmentConfig{
{Target: name},
}
})
// make sure task has been deployed.
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 1)
result := icmd.RunCmd(d.Command("ps", "-a", "-q"))
result.Assert(c, icmd.Success)
containers := strings.Split(strings.TrimSpace(result.Stdout()), "\n")
c.Assert(len(containers), checker.Equals, 1)
result = icmd.RunCmd(d.Command("inspect", "--format", `{{.NetworkSettings.Networks.foo.NetworkID}}`, containers[0]))
result.Assert(c, icmd.Success)
c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n3.ID)
// Remove Service
result = icmd.RunCmd(d.Command("service", "rm", "top"))
result.Assert(c, icmd.Success)
// make sure task has been destroyed.
waitAndAssert(c, defaultReconciliationTimeout, d.CheckActiveContainerCount, checker.Equals, 0)
result = icmd.RunCmd(d.Command("network", "rm", n1.ID))
result.Assert(c, icmd.Success)
c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n1.ID)
result = icmd.RunCmd(d.Command("network", "rm", n2.ID))
result.Assert(c, icmd.Success)
c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n2.ID)
result = icmd.RunCmd(d.Command("network", "rm", n3.ID))
result.Assert(c, icmd.Success)
c.Assert(strings.TrimSpace(result.Stdout()), checker.Equals, n3.ID)
}