2ab94e11 |
package daemon
import ( |
cc6aece1 |
"fmt"
"net" |
41071d66 |
"runtime" |
7ca635a1 |
"sort" |
2ab94e11 |
"strings" |
d59d19c3 |
"sync" |
2ab94e11 |
|
91e197d6 |
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/network" |
534a90a9 |
clustertypes "github.com/docker/docker/daemon/cluster/provider" |
d453fe35 |
"github.com/docker/docker/errdefs" |
fc2c0e62 |
"github.com/docker/docker/pkg/plugingetter" |
f15af1ef |
"github.com/docker/docker/runconfig" |
2ab94e11 |
"github.com/docker/libnetwork" |
e2ec0067 |
lncluster "github.com/docker/libnetwork/cluster" |
fc2c0e62 |
"github.com/docker/libnetwork/driverapi"
"github.com/docker/libnetwork/ipamapi" |
534a90a9 |
networktypes "github.com/docker/libnetwork/types" |
934328d8 |
"github.com/pkg/errors" |
1009e6a4 |
"github.com/sirupsen/logrus" |
99a98ccc |
"golang.org/x/net/context" |
2ab94e11 |
)
|
eb982e7c |
// NetworkControllerEnabled checks if the networking stack is enabled. |
927b334e |
// This feature depends on OS primitives and it's disabled in systems like Windows. |
eb982e7c |
func (daemon *Daemon) NetworkControllerEnabled() bool {
return daemon.netController != nil
}
|
ccc2ed01 |
// FindNetwork returns a network based on: |
cafed80c |
// 1. Full ID
// 2. Full Name
// 3. Partial ID
// as long as there is no ambiguity |
ccc2ed01 |
func (daemon *Daemon) FindNetwork(term string) (libnetwork.Network, error) { |
cafed80c |
listByFullName := []libnetwork.Network{}
listByPartialID := []libnetwork.Network{}
for _, nw := range daemon.GetNetworks() {
if nw.ID() == term {
return nw, nil
}
if nw.Name() == term {
listByFullName = append(listByFullName, nw)
}
if strings.HasPrefix(nw.ID(), term) {
listByPartialID = append(listByPartialID, nw)
} |
e52001c5 |
} |
cafed80c |
switch {
case len(listByFullName) == 1:
return listByFullName[0], nil
case len(listByFullName) > 1: |
87a12421 |
return nil, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found on name)", term, len(listByFullName))) |
cafed80c |
case len(listByPartialID) == 1:
return listByPartialID[0], nil
case len(listByPartialID) > 1: |
87a12421 |
return nil, errdefs.InvalidParameter(errors.Errorf("network %s is ambiguous (%d matches found based on ID prefix)", term, len(listByPartialID))) |
cafed80c |
} |
87a12421 |
// 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, errdefs.NotFound(libnetwork.ErrNoSuchNetwork(term)) |
2ab94e11 |
}
|
e52001c5 |
// GetNetworkByID function returns a network whose ID matches the given ID.
// It fails with an error if no matching network is found.
func (daemon *Daemon) GetNetworkByID(id string) (libnetwork.Network, error) {
c := daemon.netController
if c == nil {
return nil, libnetwork.ErrNoSuchNetwork(id) |
dd93571c |
} |
e52001c5 |
return c.NetworkByID(id) |
dd93571c |
} |
2ab94e11 |
|
dd93571c |
// GetNetworkByName function returns a network for a given network name. |
6ad4bf0a |
// If no network name is given, the default network is returned. |
dd93571c |
func (daemon *Daemon) GetNetworkByName(name string) (libnetwork.Network, error) {
c := daemon.netController |
ecffb6d5 |
if c == nil {
return nil, libnetwork.ErrNoSuchNetwork(name)
} |
dd93571c |
if name == "" {
name = c.Config().Daemon.DefaultNetwork |
2ab94e11 |
} |
dd93571c |
return c.NetworkByName(name) |
2ab94e11 |
}
|
e52001c5 |
// GetNetworksByIDPrefix returns a list of networks whose ID partially matches zero or more networks
func (daemon *Daemon) GetNetworksByIDPrefix(partialID string) []libnetwork.Network { |
2ab94e11 |
c := daemon.netController |
ecffb6d5 |
if c == nil {
return nil
} |
2ab94e11 |
list := []libnetwork.Network{}
l := func(nw libnetwork.Network) bool {
if strings.HasPrefix(nw.ID(), partialID) {
list = append(list, nw)
}
return false
}
c.WalkNetworks(l)
return list
}
|
3ca29823 |
// getAllNetworks returns a list containing all networks
func (daemon *Daemon) getAllNetworks() []libnetwork.Network { |
b249ccb1 |
c := daemon.netController
if c == nil {
return nil
}
return c.Networks() |
26dd026b |
}
|
d59d19c3 |
type ingressJob struct { |
6f4bb796 |
create *clustertypes.NetworkCreateRequest
ip net.IP
jobDone chan struct{} |
534a90a9 |
}
|
d59d19c3 |
var (
ingressWorkerOnce sync.Once
ingressJobsChannel chan *ingressJob
ingressID string
)
func (daemon *Daemon) startIngressWorker() {
ingressJobsChannel = make(chan *ingressJob, 100)
go func() { |
f7f101d5 |
// nolint: gosimple |
d59d19c3 |
for {
select {
case r := <-ingressJobsChannel:
if r.create != nil {
daemon.setupIngress(r.create, r.ip, ingressID)
ingressID = r.create.ID
} else {
daemon.releaseIngress(ingressID)
ingressID = ""
} |
6f4bb796 |
close(r.jobDone) |
d59d19c3 |
}
}
}()
} |
534a90a9 |
|
d59d19c3 |
// enqueueIngressJob adds a ingress add/rm request to the worker queue.
// It guarantees the worker is started.
func (daemon *Daemon) enqueueIngressJob(job *ingressJob) {
ingressWorkerOnce.Do(daemon.startIngressWorker)
ingressJobsChannel <- job |
534a90a9 |
}
// SetupIngress setups ingress networking. |
6f4bb796 |
// The function returns a channel which will signal the caller when the programming is completed.
func (daemon *Daemon) SetupIngress(create clustertypes.NetworkCreateRequest, nodeIP string) (<-chan struct{}, error) { |
534a90a9 |
ip, _, err := net.ParseCIDR(nodeIP)
if err != nil { |
6f4bb796 |
return nil, err |
534a90a9 |
} |
6f4bb796 |
done := make(chan struct{})
daemon.enqueueIngressJob(&ingressJob{&create, ip, done})
return done, nil |
d59d19c3 |
} |
534a90a9 |
|
d59d19c3 |
// ReleaseIngress releases the ingress networking. |
6f4bb796 |
// The function returns a channel which will signal the caller when the programming is completed.
func (daemon *Daemon) ReleaseIngress() (<-chan struct{}, error) {
done := make(chan struct{})
daemon.enqueueIngressJob(&ingressJob{nil, nil, done})
return done, nil |
d59d19c3 |
} |
534a90a9 |
|
d59d19c3 |
func (daemon *Daemon) setupIngress(create *clustertypes.NetworkCreateRequest, ip net.IP, staleID string) {
controller := daemon.netController
controller.AgentInitWait() |
534a90a9 |
|
d59d19c3 |
if staleID != "" && staleID != create.ID {
daemon.releaseIngress(staleID)
} |
a4926a4d |
|
d59d19c3 |
if _, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true); err != nil {
// If it is any other error other than already
// exists error log error and return.
if _, ok := err.(libnetwork.NetworkNameError); !ok {
logrus.Errorf("Failed creating ingress network: %v", err)
return |
534a90a9 |
} |
d59d19c3 |
// Otherwise continue down the call to create or recreate sandbox.
} |
534a90a9 |
|
41071d66 |
_, err := daemon.GetNetworkByID(create.ID) |
d59d19c3 |
if err != nil {
logrus.Errorf("Failed getting ingress network by id after creating: %v", err)
}
} |
534a90a9 |
|
d59d19c3 |
func (daemon *Daemon) releaseIngress(id string) {
controller := daemon.netController
if id == "" {
return
}
n, err := controller.NetworkByID(id)
if err != nil {
logrus.Errorf("failed to retrieve ingress network %s: %v", id, err)
return
}
if err := n.Delete(); err != nil {
logrus.Errorf("Failed to delete ingress network %s: %v", n.ID(), err)
return
} |
534a90a9 |
}
// SetNetworkBootstrapKeys sets the bootstrap keys.
func (daemon *Daemon) SetNetworkBootstrapKeys(keys []*networktypes.EncryptionKey) error { |
e2ec0067 |
err := daemon.netController.SetKeys(keys)
if err == nil {
// Upon successful key setting dispatch the keys available event
daemon.cluster.SendClusterEvent(lncluster.EventNetworkKeysAvailable)
}
return err |
534a90a9 |
}
|
99a98ccc |
// UpdateAttachment notifies the attacher about the attachment config.
func (daemon *Daemon) UpdateAttachment(networkName, networkID, containerID string, config *network.NetworkingConfig) error {
if daemon.clusterProvider == nil {
return fmt.Errorf("cluster provider is not initialized")
}
if err := daemon.clusterProvider.UpdateAttachment(networkName, containerID, config); err != nil {
return daemon.clusterProvider.UpdateAttachment(networkID, containerID, config)
}
return nil
}
// WaitForDetachment makes the cluster manager wait for detachment of
// the container from the network.
func (daemon *Daemon) WaitForDetachment(ctx context.Context, networkName, networkID, taskID, containerID string) error {
if daemon.clusterProvider == nil {
return fmt.Errorf("cluster provider is not initialized")
}
return daemon.clusterProvider.WaitForDetachment(ctx, networkName, networkID, taskID, containerID)
}
|
534a90a9 |
// CreateManagedNetwork creates an agent network.
func (daemon *Daemon) CreateManagedNetwork(create clustertypes.NetworkCreateRequest) error {
_, err := daemon.createNetwork(create.NetworkCreateRequest, create.ID, true)
return err
}
|
2ab94e11 |
// CreateNetwork creates a network with the given name, driver and other optional parameters |
b9c94b70 |
func (daemon *Daemon) CreateNetwork(create types.NetworkCreateRequest) (*types.NetworkCreateResponse, error) { |
534a90a9 |
resp, err := daemon.createNetwork(create, "", false)
if err != nil {
return nil, err
}
return resp, err
}
func (daemon *Daemon) createNetwork(create types.NetworkCreateRequest, id string, agent bool) (*types.NetworkCreateResponse, error) {
if runconfig.IsPreDefinedNetwork(create.Name) && !agent { |
3ca29823 |
err := fmt.Errorf("%s is a pre-defined network and cannot be created", create.Name) |
87a12421 |
return nil, errdefs.Forbidden(err) |
3ca29823 |
}
var warning string
nw, err := daemon.GetNetworkByName(create.Name)
if err != nil {
if _, ok := err.(libnetwork.ErrNoSuchNetwork); !ok {
return nil, err
}
}
if nw != nil { |
94b880f9 |
// check if user defined CheckDuplicate, if set true, return err
// otherwise prepare a warning message |
3ca29823 |
if create.CheckDuplicate { |
cafed80c |
if !agent || nw.Info().Dynamic() {
return nil, libnetwork.NetworkNameError(create.Name)
} |
3ca29823 |
}
warning = fmt.Sprintf("Network with name %s (id : %s) already exists", nw.Name(), nw.ID())
}
|
2ab94e11 |
c := daemon.netController |
3ca29823 |
driver := create.Driver |
2ab94e11 |
if driver == "" {
driver = c.Config().Daemon.DefaultDriver
}
|
fc214b44 |
nwOptions := []libnetwork.NetworkOption{ |
3ca29823 |
libnetwork.NetworkOptionEnableIPv6(create.EnableIPv6),
libnetwork.NetworkOptionDriverOpts(create.Options),
libnetwork.NetworkOptionLabels(create.Labels), |
abcb699a |
libnetwork.NetworkOptionAttachable(create.Attachable), |
d59d19c3 |
libnetwork.NetworkOptionIngress(create.Ingress), |
fcafc710 |
libnetwork.NetworkOptionScope(create.Scope), |
fc214b44 |
} |
8f7a8c75 |
|
9ee7b4dd |
if create.ConfigOnly {
nwOptions = append(nwOptions, libnetwork.NetworkOptionConfigOnly())
}
|
8f7a8c75 |
if create.IPAM != nil {
ipam := create.IPAM
v4Conf, v6Conf, err := getIpamConfig(ipam.Config)
if err != nil {
return nil, err
}
nwOptions = append(nwOptions, libnetwork.NetworkOptionIpam(ipam.Driver, "", v4Conf, v6Conf, ipam.Options))
}
|
3ca29823 |
if create.Internal { |
b70954e6 |
nwOptions = append(nwOptions, libnetwork.NetworkOptionInternalNetwork())
} |
534a90a9 |
if agent {
nwOptions = append(nwOptions, libnetwork.NetworkOptionDynamic())
nwOptions = append(nwOptions, libnetwork.NetworkOptionPersist(false))
}
|
9ee7b4dd |
if create.ConfigFrom != nil {
nwOptions = append(nwOptions, libnetwork.NetworkOptionConfigFrom(create.ConfigFrom.Network))
}
|
41071d66 |
if agent && driver == "overlay" && (create.Ingress || runtime.GOOS == "windows") {
nodeIP, exists := daemon.GetAttachmentStore().GetIPForNetwork(id)
if !exists {
return nil, fmt.Errorf("Failed to find a load balancer IP to use for network: %v", id)
}
nwOptions = append(nwOptions, libnetwork.NetworkOptionLBEndpoint(nodeIP))
}
|
534a90a9 |
n, err := c.NewNetwork(driver, create.Name, id, nwOptions...) |
f15af1ef |
if err != nil { |
9c77a4c2 |
if _, ok := err.(libnetwork.ErrDataStoreNotInitialized); ok { |
9b47b7b1 |
// nolint: golint |
9c77a4c2 |
return nil, errors.New("This node is not a swarm manager. Use \"docker swarm init\" or \"docker swarm join\" to connect this node to swarm and try again.")
} |
f15af1ef |
return nil, err
}
|
42860010 |
daemon.pluginRefCount(driver, driverapi.NetworkPluginEndpointType, plugingetter.Acquire) |
fc2c0e62 |
if create.IPAM != nil { |
42860010 |
daemon.pluginRefCount(create.IPAM.Driver, ipamapi.PluginEndpointType, plugingetter.Acquire) |
fc2c0e62 |
} |
f15af1ef |
daemon.LogNetworkEvent(n, "create") |
3343d234 |
|
3ca29823 |
return &types.NetworkCreateResponse{
ID: n.ID(),
Warning: warning,
}, nil |
cc6aece1 |
}
|
fc2c0e62 |
func (daemon *Daemon) pluginRefCount(driver, capability string, mode int) {
var builtinDrivers []string
if capability == driverapi.NetworkPluginEndpointType {
builtinDrivers = daemon.netController.BuiltinDrivers()
} else if capability == ipamapi.PluginEndpointType {
builtinDrivers = daemon.netController.BuiltinIPAMDrivers()
}
for _, d := range builtinDrivers {
if d == driver {
return
}
}
if daemon.PluginStore != nil {
_, err := daemon.PluginStore.Get(driver, capability, mode)
if err != nil {
logrus.WithError(err).WithFields(logrus.Fields{"mode": mode, "driver": driver}).Error("Error handling plugin refcount operation")
}
}
}
|
cc6aece1 |
func getIpamConfig(data []network.IPAMConfig) ([]*libnetwork.IpamConf, []*libnetwork.IpamConf, error) {
ipamV4Cfg := []*libnetwork.IpamConf{}
ipamV6Cfg := []*libnetwork.IpamConf{}
for _, d := range data {
iCfg := libnetwork.IpamConf{}
iCfg.PreferredPool = d.Subnet
iCfg.SubPool = d.IPRange
iCfg.Gateway = d.Gateway
iCfg.AuxAddresses = d.AuxAddress
ip, _, err := net.ParseCIDR(d.Subnet)
if err != nil {
return nil, nil, fmt.Errorf("Invalid subnet %s : %v", d.Subnet, err)
}
if ip.To4() != nil {
ipamV4Cfg = append(ipamV4Cfg, &iCfg)
} else {
ipamV6Cfg = append(ipamV6Cfg, &iCfg)
}
}
return ipamV4Cfg, ipamV6Cfg, nil |
2ab94e11 |
} |
a0398fbd |
|
534a90a9 |
// UpdateContainerServiceConfig updates a service configuration.
func (daemon *Daemon) UpdateContainerServiceConfig(containerName string, serviceConfig *clustertypes.ServiceConfig) error {
container, err := daemon.GetContainer(containerName)
if err != nil {
return err
}
container.NetworkSettings.Service = serviceConfig
return nil
}
|
a0398fbd |
// ConnectContainerToNetwork connects the given container to the given
// network. If either cannot be found, an err is returned. If the
// network cannot be set up, an err is returned. |
2bb3fc1b |
func (daemon *Daemon) ConnectContainerToNetwork(containerName, networkName string, endpointConfig *network.EndpointSettings) error { |
d7d512bb |
container, err := daemon.GetContainer(containerName) |
a0398fbd |
if err != nil {
return err
} |
2bb3fc1b |
return daemon.ConnectToNetwork(container, networkName, endpointConfig) |
a0398fbd |
}
// DisconnectContainerFromNetwork disconnects the given container from
// the given network. If either cannot be found, an err is returned. |
05a3f266 |
func (daemon *Daemon) DisconnectContainerFromNetwork(containerName string, networkName string, force bool) error { |
d7d512bb |
container, err := daemon.GetContainer(containerName) |
a0398fbd |
if err != nil { |
b464f1d7 |
if force { |
05a3f266 |
return daemon.ForceEndpointDelete(containerName, networkName) |
b464f1d7 |
} |
a0398fbd |
return err
} |
05a3f266 |
return daemon.DisconnectFromNetwork(container, networkName, force) |
a0398fbd |
} |
aa7fd884 |
// GetNetworkDriverList returns the list of plugins drivers
// registered for network. |
7ca635a1 |
func (daemon *Daemon) GetNetworkDriverList() []string { |
37d2a700 |
if !daemon.NetworkControllerEnabled() {
return nil
} |
ea266f8f |
|
5e9c78ae |
pluginList := daemon.netController.BuiltinDrivers() |
6aaa85f0 |
managedPlugins := daemon.PluginStore.GetAllManagedPluginsByCap(driverapi.NetworkPluginEndpointType)
for _, plugin := range managedPlugins {
pluginList = append(pluginList, plugin.Name())
}
|
5e9c78ae |
pluginMap := make(map[string]bool) |
3347aba9 |
for _, plugin := range pluginList {
pluginMap[plugin] = true
} |
ea266f8f |
|
7ca635a1 |
networks := daemon.netController.Networks() |
aa7fd884 |
for _, network := range networks { |
7ca635a1 |
if !pluginMap[network.Type()] {
pluginList = append(pluginList, network.Type())
pluginMap[network.Type()] = true
} |
aa7fd884 |
} |
7ca635a1 |
sort.Strings(pluginList) |
aa7fd884 |
return pluginList
} |
f15af1ef |
|
534a90a9 |
// DeleteManagedNetwork deletes an agent network. |
cafed80c |
// The requirement of networkID is enforced. |
534a90a9 |
func (daemon *Daemon) DeleteManagedNetwork(networkID string) error { |
cafed80c |
n, err := daemon.GetNetworkByID(networkID)
if err != nil {
return err
}
return daemon.deleteNetwork(n, true) |
534a90a9 |
}
|
f15af1ef |
// DeleteNetwork destroys a network unless it's one of docker's predefined networks.
func (daemon *Daemon) DeleteNetwork(networkID string) error { |
cafed80c |
n, err := daemon.GetNetworkByID(networkID) |
f15af1ef |
if err != nil {
return err
} |
cafed80c |
return daemon.deleteNetwork(n, false)
} |
f15af1ef |
|
cafed80c |
func (daemon *Daemon) deleteLoadBalancerSandbox(n libnetwork.Network) {
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.
} |
41071d66 |
} |
cafed80c |
} |
41071d66 |
|
cafed80c |
func (daemon *Daemon) deleteNetwork(nw libnetwork.Network, dynamic bool) error { |
534a90a9 |
if runconfig.IsPreDefinedNetwork(nw.Name()) && !dynamic { |
a793564b |
err := fmt.Errorf("%s is a pre-defined network and cannot be removed", nw.Name()) |
87a12421 |
return errdefs.Forbidden(err) |
f15af1ef |
}
|
b34d3e73 |
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()) |
87a12421 |
return errdefs.Forbidden(err) |
b34d3e73 |
}
|
f15af1ef |
if err := nw.Delete(); err != nil {
return err
} |
9ee7b4dd |
// If this is not a configuration only network, we need to
// update the corresponding remote drivers' reference counts
if !nw.Info().ConfigOnly() {
daemon.pluginRefCount(nw.Type(), driverapi.NetworkPluginEndpointType, plugingetter.Release)
ipamType, _, _, _ := nw.Info().IpamConfig()
daemon.pluginRefCount(ipamType, ipamapi.PluginEndpointType, plugingetter.Release)
daemon.LogNetworkEvent(nw, "destroy")
}
|
f15af1ef |
return nil
} |
3ca29823 |
|
534a90a9 |
// GetNetworks returns a list of all networks
func (daemon *Daemon) GetNetworks() []libnetwork.Network {
return daemon.getAllNetworks() |
3ca29823 |
} |
3cedca5d |
// clearAttachableNetworks removes the attachable networks
// after disconnecting any connected container
func (daemon *Daemon) clearAttachableNetworks() {
for _, n := range daemon.GetNetworks() {
if !n.Info().Attachable() {
continue
}
for _, ep := range n.Endpoints() {
epInfo := ep.Info()
if epInfo == nil {
continue
}
sb := epInfo.Sandbox()
if sb == nil {
continue
}
containerID := sb.ContainerID()
if err := daemon.DisconnectContainerFromNetwork(containerID, n.ID(), true); err != nil {
logrus.Warnf("Failed to disconnect container %s from swarm network %s on cluster leave: %v",
containerID, n.Name(), err)
}
}
if err := daemon.DeleteManagedNetwork(n.ID()); err != nil {
logrus.Warnf("Failed to remove swarm network %s on cluster leave: %v", n.Name(), err)
}
}
} |