6 "github.com/Sirupsen/logrus"
7 apierrors "github.com/docker/docker/api/errors"
8 apitypes "github.com/docker/docker/api/types"
9 "github.com/docker/docker/api/types/network"
10 types "github.com/docker/docker/api/types/swarm"
11 "github.com/docker/docker/daemon/cluster/convert"
12 "github.com/docker/docker/runconfig"
13 swarmapi "github.com/docker/swarmkit/api"
14 "github.com/pkg/errors"
15 "golang.org/x/net/context"
18 // GetNetworks returns all current cluster managed networks.
19 func (c *Cluster) GetNetworks() ([]apitypes.NetworkResource, error) {
20 list, err := c.getNetworks(nil)
24 removePredefinedNetworks(&list)
28 func removePredefinedNetworks(networks *[]apitypes.NetworkResource) {
33 for i, n := range *networks {
34 if v, ok := n.Labels["com.docker.swarm.predefined"]; ok && v == "true" {
35 idxs = append(idxs, i)
38 for i, idx := range idxs {
40 *networks = append((*networks)[:idx], (*networks)[idx+1:]...)
44 func (c *Cluster) getNetworks(filters *swarmapi.ListNetworksRequest_Filters) ([]apitypes.NetworkResource, error) {
48 state := c.currentNodeState()
49 if !state.IsActiveManager() {
50 return nil, c.errNoManager(state)
53 ctx, cancel := c.getRequestContext()
56 r, err := state.controlClient.ListNetworks(ctx, &swarmapi.ListNetworksRequest{Filters: filters})
61 networks := make([]apitypes.NetworkResource, 0, len(r.Networks))
63 for _, network := range r.Networks {
64 networks = append(networks, convert.BasicNetworkFromGRPC(*network))
70 // GetNetwork returns a cluster network by an ID.
71 func (c *Cluster) GetNetwork(input string) (apitypes.NetworkResource, error) {
72 var network *swarmapi.Network
74 if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
75 n, err := getNetwork(ctx, state.controlClient, input)
82 return apitypes.NetworkResource{}, err
84 return convert.BasicNetworkFromGRPC(*network), nil
87 // GetNetworksByName returns cluster managed networks by name.
88 // It is ok to have multiple networks here. #18864
89 func (c *Cluster) GetNetworksByName(name string) ([]apitypes.NetworkResource, error) {
90 // Note that swarmapi.GetNetworkRequest.Name is not functional.
91 // So we cannot just use that with c.GetNetwork.
92 return c.getNetworks(&swarmapi.ListNetworksRequest_Filters{
93 Names: []string{name},
97 func attacherKey(target, containerID string) string {
98 return containerID + ":" + target
101 // UpdateAttachment signals the attachment config to the attachment
102 // waiter who is trying to start or attach the container to the
104 func (c *Cluster) UpdateAttachment(target, containerID string, config *network.NetworkingConfig) error {
106 attacher, ok := c.attachers[attacherKey(target, containerID)]
107 if !ok || attacher == nil {
109 return fmt.Errorf("could not find attacher for container %s to network %s", containerID, target)
111 if attacher.inProgress {
112 logrus.Debugf("Discarding redundant notice of resource allocation on network %s for task id %s", target, attacher.taskID)
116 attacher.inProgress = true
119 attacher.attachWaitCh <- config
124 // WaitForDetachment waits for the container to stop or detach from
126 func (c *Cluster) WaitForDetachment(ctx context.Context, networkName, networkID, taskID, containerID string) error {
128 attacher, ok := c.attachers[attacherKey(networkName, containerID)]
130 attacher, ok = c.attachers[attacherKey(networkID, containerID)]
132 state := c.currentNodeState()
133 if state.swarmNode == nil || state.swarmNode.Agent() == nil {
135 return errors.New("invalid cluster node while waiting for detachment")
139 agent := state.swarmNode.Agent()
140 if ok && attacher != nil &&
141 attacher.detachWaitCh != nil &&
142 attacher.attachCompleteCh != nil {
143 // Attachment may be in progress still so wait for
144 // attachment to complete.
146 case <-attacher.attachCompleteCh:
151 if attacher.taskID == taskID {
153 case <-attacher.detachWaitCh:
160 return agent.ResourceAllocator().DetachNetwork(ctx, taskID)
163 // AttachNetwork generates an attachment request towards the manager.
164 func (c *Cluster) AttachNetwork(target string, containerID string, addresses []string) (*network.NetworkingConfig, error) {
165 aKey := attacherKey(target, containerID)
167 state := c.currentNodeState()
168 if state.swarmNode == nil || state.swarmNode.Agent() == nil {
170 return nil, errors.New("invalid cluster node while attaching to network")
172 if attacher, ok := c.attachers[aKey]; ok {
174 return attacher.config, nil
177 agent := state.swarmNode.Agent()
178 attachWaitCh := make(chan *network.NetworkingConfig)
179 detachWaitCh := make(chan struct{})
180 attachCompleteCh := make(chan struct{})
181 c.attachers[aKey] = &attacher{
182 attachWaitCh: attachWaitCh,
183 attachCompleteCh: attachCompleteCh,
184 detachWaitCh: detachWaitCh,
188 ctx, cancel := c.getRequestContext()
191 taskID, err := agent.ResourceAllocator().AttachNetwork(ctx, containerID, target, addresses)
194 delete(c.attachers, aKey)
196 return nil, fmt.Errorf("Could not attach to network %s: %v", target, err)
200 c.attachers[aKey].taskID = taskID
201 close(attachCompleteCh)
204 logrus.Debugf("Successfully attached to network %s with task id %s", target, taskID)
207 ctx, cancel := c.getRequestContext()
209 if err := agent.ResourceAllocator().DetachNetwork(ctx, taskID); err != nil {
210 logrus.Errorf("Failed remove network attachment %s to network %s on allocation failure: %v",
215 var config *network.NetworkingConfig
217 case config = <-attachWaitCh:
220 return nil, fmt.Errorf("attaching to network failed, make sure your network options are correct and check manager logs: %v", ctx.Err())
224 c.attachers[aKey].config = config
227 logrus.Debugf("Successfully allocated resources on network %s for task id %s", target, taskID)
232 // DetachNetwork unblocks the waiters waiting on WaitForDetachment so
233 // that a request to detach can be generated towards the manager.
234 func (c *Cluster) DetachNetwork(target string, containerID string) error {
235 aKey := attacherKey(target, containerID)
238 attacher, ok := c.attachers[aKey]
239 delete(c.attachers, aKey)
243 return fmt.Errorf("could not find network attachment for container %s to network %s", containerID, target)
246 close(attacher.detachWaitCh)
250 // CreateNetwork creates a new cluster managed network.
251 func (c *Cluster) CreateNetwork(s apitypes.NetworkCreateRequest) (string, error) {
252 if runconfig.IsPreDefinedNetwork(s.Name) {
253 err := fmt.Errorf("%s is a pre-defined network and cannot be created", s.Name)
254 return "", apierrors.NewRequestForbiddenError(err)
257 var resp *swarmapi.CreateNetworkResponse
258 if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
259 networkSpec := convert.BasicNetworkCreateToGRPC(s)
260 r, err := state.controlClient.CreateNetwork(ctx, &swarmapi.CreateNetworkRequest{Spec: &networkSpec})
270 return resp.Network.ID, nil
273 // RemoveNetwork removes a cluster network.
274 func (c *Cluster) RemoveNetwork(input string) error {
275 return c.lockedManagerAction(func(ctx context.Context, state nodeState) error {
276 network, err := getNetwork(ctx, state.controlClient, input)
281 _, err = state.controlClient.RemoveNetwork(ctx, &swarmapi.RemoveNetworkRequest{NetworkID: network.ID})
286 func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.ControlClient, s *types.ServiceSpec) error {
287 // Always prefer NetworkAttachmentConfigs from TaskTemplate
288 // but fallback to service spec for backward compatibility
289 networks := s.TaskTemplate.Networks
290 if len(networks) == 0 {
291 networks = s.Networks
293 for i, n := range networks {
294 apiNetwork, err := getNetwork(ctx, client, n.Target)
296 ln, _ := c.config.Backend.FindNetwork(n.Target)
297 if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) {
298 // Need to retrieve the corresponding predefined swarm network
299 // and use its id for the request.
300 apiNetwork, err = getNetwork(ctx, client, ln.Name())
302 err = fmt.Errorf("could not find the corresponding predefined swarm network: %v", err)
303 return apierrors.NewRequestNotFoundError(err)
307 if ln != nil && !ln.Info().Dynamic() {
308 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())
309 return apierrors.NewRequestForbiddenError(err)
314 networks[i].Target = apiNetwork.ID