From b34d3e730fe3eee4b058e4dc165a7d4964e00a2a Mon Sep 17 00:00:00 2001 From: Alessandro Boch Date: Mon, 1 May 2017 16:44:04 -0700 Subject: [PATCH] Integrate local datascope network with swarm Signed-off-by: Alessandro Boch --- api/types/swarm/network.go | 18 ++++-- daemon/cluster/convert/network.go | 19 +++++- daemon/cluster/executor/container/adapter.go | 9 ++- .../cluster/executor/container/container.go | 64 +++++++++++++------ daemon/cluster/networks.go | 38 ++++++++++- daemon/network.go | 10 +++ 6 files changed, 124 insertions(+), 34 deletions(-) diff --git a/api/types/swarm/network.go b/api/types/swarm/network.go index 693f85cce1..2bac19b7a1 100644 --- a/api/types/swarm/network.go +++ b/api/types/swarm/network.go @@ -1,5 +1,9 @@ package swarm +import ( + "github.com/docker/docker/api/types/network" +) + // Endpoint represents an endpoint. type Endpoint struct { Spec EndpointSpec `json:",omitempty"` @@ -78,12 +82,14 @@ type Network struct { // NetworkSpec represents the spec of a network. type NetworkSpec struct { Annotations - DriverConfiguration *Driver `json:",omitempty"` - IPv6Enabled bool `json:",omitempty"` - Internal bool `json:",omitempty"` - Attachable bool `json:",omitempty"` - Ingress bool `json:",omitempty"` - IPAMOptions *IPAMOptions `json:",omitempty"` + DriverConfiguration *Driver `json:",omitempty"` + IPv6Enabled bool `json:",omitempty"` + Internal bool `json:",omitempty"` + Attachable bool `json:",omitempty"` + Ingress bool `json:",omitempty"` + IPAMOptions *IPAMOptions `json:",omitempty"` + ConfigFrom *network.ConfigReference `json:",omitempty"` + Scope string `json:",omitempty"` } // NetworkAttachmentConfig represents the configuration of a network attachment. diff --git a/daemon/cluster/convert/network.go b/daemon/cluster/convert/network.go index 6e28b172f3..0ce450b749 100644 --- a/daemon/cluster/convert/network.go +++ b/daemon/cluster/convert/network.go @@ -6,6 +6,7 @@ import ( basictypes "github.com/docker/docker/api/types" networktypes "github.com/docker/docker/api/types/network" types "github.com/docker/docker/api/types/swarm" + netconst "github.com/docker/libnetwork/datastore" swarmapi "github.com/docker/swarmkit/api" gogotypes "github.com/gogo/protobuf/types" ) @@ -30,10 +31,17 @@ func networkFromGRPC(n *swarmapi.Network) types.Network { Attachable: n.Spec.Attachable, Ingress: n.Spec.Ingress, IPAMOptions: ipamFromGRPC(n.Spec.IPAM), + Scope: netconst.SwarmScope, }, IPAMOptions: ipamFromGRPC(n.IPAM), } + if n.Spec.ConfigFrom != "" { + network.Spec.ConfigFrom = &networktypes.ConfigReference{ + Network: n.Spec.ConfigFrom, + } + } + // Meta network.Version.Index = n.Meta.Version.Index network.CreatedAt, _ = gogotypes.TimestampFromProto(n.Meta.CreatedAt) @@ -152,7 +160,7 @@ func BasicNetworkFromGRPC(n swarmapi.Network) basictypes.NetworkResource { nr := basictypes.NetworkResource{ ID: n.ID, Name: n.Spec.Annotations.Name, - Scope: "swarm", + Scope: netconst.SwarmScope, EnableIPv6: spec.Ipv6Enabled, IPAM: ipam, Internal: spec.Internal, @@ -161,6 +169,12 @@ func BasicNetworkFromGRPC(n swarmapi.Network) basictypes.NetworkResource { Labels: n.Spec.Annotations.Labels, } + if n.Spec.ConfigFrom != "" { + nr.ConfigFrom = networktypes.ConfigReference{ + Network: n.Spec.ConfigFrom, + } + } + if n.DriverState != nil { nr.Driver = n.DriverState.Name nr.Options = n.DriverState.Options @@ -206,5 +220,8 @@ func BasicNetworkCreateToGRPC(create basictypes.NetworkCreateRequest) swarmapi.N } ns.IPAM.Configs = ipamSpec } + if create.ConfigFrom != nil { + ns.ConfigFrom = create.ConfigFrom.Network + } return ns } diff --git a/daemon/cluster/executor/container/adapter.go b/daemon/cluster/executor/container/adapter.go index fd2ef354a6..67d42c706e 100644 --- a/daemon/cluster/executor/container/adapter.go +++ b/daemon/cluster/executor/container/adapter.go @@ -176,7 +176,7 @@ func (c *containerAdapter) removeNetworks(ctx context.Context) error { } func (c *containerAdapter) networkAttach(ctx context.Context) error { - config := c.container.createNetworkingConfig() + config := c.container.createNetworkingConfig(c.backend) var ( networkName string @@ -195,7 +195,7 @@ func (c *containerAdapter) networkAttach(ctx context.Context) error { } func (c *containerAdapter) waitForDetach(ctx context.Context) error { - config := c.container.createNetworkingConfig() + config := c.container.createNetworkingConfig(c.backend) var ( networkName string @@ -216,20 +216,19 @@ func (c *containerAdapter) waitForDetach(ctx context.Context) error { func (c *containerAdapter) create(ctx context.Context) error { var cr containertypes.ContainerCreateCreatedBody var err error - if cr, err = c.backend.CreateManagedContainer(types.ContainerCreateConfig{ Name: c.container.name(), Config: c.container.config(), HostConfig: c.container.hostConfig(), // Use the first network in container create - NetworkingConfig: c.container.createNetworkingConfig(), + NetworkingConfig: c.container.createNetworkingConfig(c.backend), }); err != nil { return err } // Docker daemon currently doesn't support multiple networks in container create // Connect to all other networks - nc := c.container.connectNetworkingConfig() + nc := c.container.connectNetworkingConfig(c.backend) if nc != nil { for n, ep := range nc.EndpointsConfig { diff --git a/daemon/cluster/executor/container/container.go b/daemon/cluster/executor/container/container.go index dcac7281b2..ca3ed06596 100644 --- a/daemon/cluster/executor/container/container.go +++ b/daemon/cluster/executor/container/container.go @@ -18,8 +18,10 @@ import ( enginemount "github.com/docker/docker/api/types/mount" "github.com/docker/docker/api/types/network" volumetypes "github.com/docker/docker/api/types/volume" + executorpkg "github.com/docker/docker/daemon/cluster/executor" clustertypes "github.com/docker/docker/daemon/cluster/provider" "github.com/docker/go-connections/nat" + netconst "github.com/docker/libnetwork/datastore" "github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/api" "github.com/docker/swarmkit/template" @@ -374,6 +376,14 @@ func (c *containerConfig) hostConfig() *enginecontainer.HostConfig { } } + if len(c.task.Networks) > 0 { + labels := c.task.Networks[0].Network.Spec.Annotations.Labels + name := c.task.Networks[0].Network.Spec.Annotations.Name + if v, ok := labels["com.docker.swarm.predefined"]; ok && v == "true" { + hc.NetworkMode = enginecontainer.NetworkMode(name) + } + } + return hc } @@ -428,7 +438,7 @@ func (c *containerConfig) resources() enginecontainer.Resources { } // Docker daemon supports just 1 network during container create. -func (c *containerConfig) createNetworkingConfig() *network.NetworkingConfig { +func (c *containerConfig) createNetworkingConfig(b executorpkg.Backend) *network.NetworkingConfig { var networks []*api.NetworkAttachment if c.task.Spec.GetContainer() != nil || c.task.Spec.GetAttachment() != nil { networks = c.task.Networks @@ -436,19 +446,18 @@ func (c *containerConfig) createNetworkingConfig() *network.NetworkingConfig { epConfig := make(map[string]*network.EndpointSettings) if len(networks) > 0 { - epConfig[networks[0].Network.Spec.Annotations.Name] = getEndpointConfig(networks[0]) + epConfig[networks[0].Network.Spec.Annotations.Name] = getEndpointConfig(networks[0], b) } return &network.NetworkingConfig{EndpointsConfig: epConfig} } // TODO: Merge this function with createNetworkingConfig after daemon supports multiple networks in container create -func (c *containerConfig) connectNetworkingConfig() *network.NetworkingConfig { +func (c *containerConfig) connectNetworkingConfig(b executorpkg.Backend) *network.NetworkingConfig { var networks []*api.NetworkAttachment if c.task.Spec.GetContainer() != nil { networks = c.task.Networks } - // First network is used during container create. Other networks are used in "docker network connect" if len(networks) < 2 { return nil @@ -456,12 +465,12 @@ func (c *containerConfig) connectNetworkingConfig() *network.NetworkingConfig { epConfig := make(map[string]*network.EndpointSettings) for _, na := range networks[1:] { - epConfig[na.Network.Spec.Annotations.Name] = getEndpointConfig(na) + epConfig[na.Network.Spec.Annotations.Name] = getEndpointConfig(na, b) } return &network.NetworkingConfig{EndpointsConfig: epConfig} } -func getEndpointConfig(na *api.NetworkAttachment) *network.EndpointSettings { +func getEndpointConfig(na *api.NetworkAttachment, b executorpkg.Backend) *network.EndpointSettings { var ipv4, ipv6 string for _, addr := range na.Addresses { ip, _, err := net.ParseCIDR(addr) @@ -479,13 +488,19 @@ func getEndpointConfig(na *api.NetworkAttachment) *network.EndpointSettings { } } - return &network.EndpointSettings{ + n := &network.EndpointSettings{ NetworkID: na.Network.ID, IPAMConfig: &network.EndpointIPAMConfig{ IPv4Address: ipv4, IPv6Address: ipv6, }, } + 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 { + n.NetworkID = ln.ID() + } + } + return n } func (c *containerConfig) virtualIP(networkID string) string { @@ -570,27 +585,38 @@ func (c *containerConfig) networkCreateRequest(name string) (clustertypes.Networ options := types.NetworkCreate{ // ID: na.Network.ID, - Driver: na.Network.DriverState.Name, - IPAM: &network.IPAM{ - Driver: na.Network.IPAM.Driver.Name, - Options: na.Network.IPAM.Driver.Options, - }, - Options: na.Network.DriverState.Options, Labels: na.Network.Spec.Annotations.Labels, Internal: na.Network.Spec.Internal, Attachable: na.Network.Spec.Attachable, Ingress: na.Network.Spec.Ingress, EnableIPv6: na.Network.Spec.Ipv6Enabled, CheckDuplicate: true, + Scope: netconst.SwarmScope, } - for _, ic := range na.Network.IPAM.Configs { - c := network.IPAMConfig{ - Subnet: ic.Subnet, - IPRange: ic.Range, - Gateway: ic.Gateway, + if na.Network.Spec.ConfigFrom != "" { + options.ConfigFrom = &network.ConfigReference{ + Network: na.Network.Spec.ConfigFrom, + } + } + + if na.Network.DriverState != nil { + options.Driver = na.Network.DriverState.Name + options.Options = na.Network.DriverState.Options + } + if na.Network.IPAM != nil { + options.IPAM = &network.IPAM{ + Driver: na.Network.IPAM.Driver.Name, + Options: na.Network.IPAM.Driver.Options, + } + for _, ic := range na.Network.IPAM.Configs { + c := network.IPAMConfig{ + Subnet: ic.Subnet, + IPRange: ic.Range, + Gateway: ic.Gateway, + } + options.IPAM.Config = append(options.IPAM.Config, c) } - options.IPAM.Config = append(options.IPAM.Config, c) } return clustertypes.NetworkCreateRequest{ diff --git a/daemon/cluster/networks.go b/daemon/cluster/networks.go index f478e650cf..1906c37bd2 100644 --- a/daemon/cluster/networks.go +++ b/daemon/cluster/networks.go @@ -17,7 +17,28 @@ import ( // GetNetworks returns all current cluster managed networks. func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) { - return c.getNetworks(nil) + list, err := c.getNetworks(nil) + if err != nil { + return nil, err + } + removePredefinedNetworks(&list) + return list, nil +} + +func removePredefinedNetworks(networks *[]apitypes.NetworkResource) { + if networks == nil { + return + } + var idxs []int + for i, n := range *networks { + if v, ok := n.Labels["com.docker.swarm.predefined"]; ok && v == "true" { + idxs = append(idxs, i) + } + } + for i, idx := range idxs { + idx -= i + *networks = append((*networks)[:idx], (*networks)[idx+1:]...) + } } func (c *Cluster) getNetworks(filters *swarmapi.ListNetworksRequest_Filters) ([]apitypes.NetworkResource, error) { @@ -269,16 +290,27 @@ func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.Control if len(networks) == 0 { networks = s.Networks } - for i, n := range networks { apiNetwork, err := getNetwork(ctx, client, n.Target) if err != nil { - if ln, _ := c.config.Backend.FindNetwork(n.Target); ln != nil && !ln.Info().Dynamic() { + ln, _ := c.config.Backend.FindNetwork(n.Target) + if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) { + // Need to retrieve the corresponding predefined swarm network + // and use its id for the request. + apiNetwork, err = getNetwork(ctx, client, ln.Name()) + if err != nil { + err = fmt.Errorf("could not find the corresponding predefined swarm network: %v", err) + return apierrors.NewRequestNotFoundError(err) + } + goto setid + } + if ln != nil && !ln.Info().Dynamic() { err = fmt.Errorf("The network %s cannot be used with services. Only networks scoped to the swarm can be used, such as those created with the overlay driver.", ln.Name()) return apierrors.NewRequestForbiddenError(err) } return err } + setid: networks[i].Target = apiNetwork.ID } return nil diff --git a/daemon/network.go b/daemon/network.go index ca8d37f416..366c2a59e4 100644 --- a/daemon/network.go +++ b/daemon/network.go @@ -507,6 +507,16 @@ func (daemon *Daemon) deleteNetwork(networkID string, dynamic bool) error { return apierrors.NewRequestForbiddenError(err) } + if dynamic && !nw.Info().Dynamic() { + if runconfig.IsPreDefinedNetwork(nw.Name()) { + // Predefined networks now support swarm services. Make this + // a no-op when cluster requests to remove the predefined network. + return nil + } + err := fmt.Errorf("%s is not a dynamic network", nw.Name()) + return apierrors.NewRequestForbiddenError(err) + } + if err := nw.Delete(); err != nil { return err }