github.com/moby/docker@v26.1.3+incompatible/daemon/cluster/networks.go (about) 1 package cluster // import "github.com/docker/docker/daemon/cluster" 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/containerd/log" 8 apitypes "github.com/docker/docker/api/types" 9 "github.com/docker/docker/api/types/filters" 10 "github.com/docker/docker/api/types/network" 11 types "github.com/docker/docker/api/types/swarm" 12 "github.com/docker/docker/daemon/cluster/convert" 13 internalnetwork "github.com/docker/docker/daemon/network" 14 "github.com/docker/docker/errdefs" 15 "github.com/docker/docker/internal/compatcontext" 16 "github.com/docker/docker/runconfig" 17 swarmapi "github.com/moby/swarmkit/v2/api" 18 "github.com/pkg/errors" 19 ) 20 21 // GetNetworks returns all current cluster managed networks. 22 func (c *Cluster) GetNetworks(filter filters.Args) ([]apitypes.NetworkResource, error) { 23 var f *swarmapi.ListNetworksRequest_Filters 24 25 if filter.Len() > 0 { 26 f = &swarmapi.ListNetworksRequest_Filters{} 27 28 if filter.Contains("name") { 29 f.Names = filter.Get("name") 30 f.NamePrefixes = filter.Get("name") 31 } 32 33 if filter.Contains("id") { 34 f.IDPrefixes = filter.Get("id") 35 } 36 } 37 38 list, err := c.getNetworks(f) 39 if err != nil { 40 return nil, err 41 } 42 filterPredefinedNetworks(&list) 43 44 return internalnetwork.FilterNetworks(list, filter) 45 } 46 47 func filterPredefinedNetworks(networks *[]apitypes.NetworkResource) { 48 if networks == nil { 49 return 50 } 51 var idxs []int 52 for i, n := range *networks { 53 if v, ok := n.Labels["com.docker.swarm.predefined"]; ok && v == "true" { 54 idxs = append(idxs, i) 55 } 56 } 57 for i, idx := range idxs { 58 idx -= i 59 *networks = append((*networks)[:idx], (*networks)[idx+1:]...) 60 } 61 } 62 63 func (c *Cluster) getNetworks(filters *swarmapi.ListNetworksRequest_Filters) ([]apitypes.NetworkResource, error) { 64 c.mu.RLock() 65 defer c.mu.RUnlock() 66 67 state := c.currentNodeState() 68 if !state.IsActiveManager() { 69 return nil, c.errNoManager(state) 70 } 71 72 ctx := context.TODO() 73 ctx, cancel := c.getRequestContext(ctx) 74 defer cancel() 75 76 r, err := state.controlClient.ListNetworks(ctx, &swarmapi.ListNetworksRequest{Filters: filters}) 77 if err != nil { 78 return nil, err 79 } 80 81 networks := make([]apitypes.NetworkResource, 0, len(r.Networks)) 82 83 for _, network := range r.Networks { 84 networks = append(networks, convert.BasicNetworkFromGRPC(*network)) 85 } 86 87 return networks, nil 88 } 89 90 // GetNetwork returns a cluster network by an ID. 91 func (c *Cluster) GetNetwork(input string) (apitypes.NetworkResource, error) { 92 var network *swarmapi.Network 93 94 if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error { 95 n, err := getNetwork(ctx, state.controlClient, input) 96 if err != nil { 97 return err 98 } 99 network = n 100 return nil 101 }); err != nil { 102 return apitypes.NetworkResource{}, err 103 } 104 return convert.BasicNetworkFromGRPC(*network), nil 105 } 106 107 // GetNetworksByName returns cluster managed networks by name. 108 // It is ok to have multiple networks here. #18864 109 func (c *Cluster) GetNetworksByName(name string) ([]apitypes.NetworkResource, error) { 110 // Note that swarmapi.GetNetworkRequest.Name is not functional. 111 // So we cannot just use that with c.GetNetwork. 112 return c.getNetworks(&swarmapi.ListNetworksRequest_Filters{ 113 Names: []string{name}, 114 }) 115 } 116 117 func attacherKey(target, containerID string) string { 118 return containerID + ":" + target 119 } 120 121 // UpdateAttachment signals the attachment config to the attachment 122 // waiter who is trying to start or attach the container to the 123 // network. 124 func (c *Cluster) UpdateAttachment(target, containerID string, config *network.NetworkingConfig) error { 125 c.mu.Lock() 126 attacher, ok := c.attachers[attacherKey(target, containerID)] 127 if !ok || attacher == nil { 128 c.mu.Unlock() 129 return fmt.Errorf("could not find attacher for container %s to network %s", containerID, target) 130 } 131 if attacher.inProgress { 132 log.G(context.TODO()).Debugf("Discarding redundant notice of resource allocation on network %s for task id %s", target, attacher.taskID) 133 c.mu.Unlock() 134 return nil 135 } 136 attacher.inProgress = true 137 c.mu.Unlock() 138 139 attacher.attachWaitCh <- config 140 141 return nil 142 } 143 144 // WaitForDetachment waits for the container to stop or detach from 145 // the network. 146 func (c *Cluster) WaitForDetachment(ctx context.Context, networkName, networkID, taskID, containerID string) error { 147 c.mu.RLock() 148 attacher, ok := c.attachers[attacherKey(networkName, containerID)] 149 if !ok { 150 attacher, ok = c.attachers[attacherKey(networkID, containerID)] 151 } 152 state := c.currentNodeState() 153 if state.swarmNode == nil || state.swarmNode.Agent() == nil { 154 c.mu.RUnlock() 155 return errors.New("invalid cluster node while waiting for detachment") 156 } 157 158 c.mu.RUnlock() 159 agent := state.swarmNode.Agent() 160 if ok && attacher != nil && 161 attacher.detachWaitCh != nil && 162 attacher.attachCompleteCh != nil { 163 // Attachment may be in progress still so wait for 164 // attachment to complete. 165 select { 166 case <-attacher.attachCompleteCh: 167 case <-ctx.Done(): 168 return ctx.Err() 169 } 170 171 if attacher.taskID == taskID { 172 select { 173 case <-attacher.detachWaitCh: 174 case <-ctx.Done(): 175 return ctx.Err() 176 } 177 } 178 } 179 180 return agent.ResourceAllocator().DetachNetwork(ctx, taskID) 181 } 182 183 // AttachNetwork generates an attachment request towards the manager. 184 func (c *Cluster) AttachNetwork(target string, containerID string, addresses []string) (*network.NetworkingConfig, error) { 185 aKey := attacherKey(target, containerID) 186 c.mu.Lock() 187 state := c.currentNodeState() 188 if state.swarmNode == nil || state.swarmNode.Agent() == nil { 189 c.mu.Unlock() 190 return nil, errors.New("invalid cluster node while attaching to network") 191 } 192 if attacher, ok := c.attachers[aKey]; ok { 193 c.mu.Unlock() 194 return attacher.config, nil 195 } 196 197 agent := state.swarmNode.Agent() 198 attachWaitCh := make(chan *network.NetworkingConfig) 199 detachWaitCh := make(chan struct{}) 200 attachCompleteCh := make(chan struct{}) 201 c.attachers[aKey] = &attacher{ 202 attachWaitCh: attachWaitCh, 203 attachCompleteCh: attachCompleteCh, 204 detachWaitCh: detachWaitCh, 205 } 206 c.mu.Unlock() 207 208 ctx := context.TODO() 209 ctx, cancel := c.getRequestContext(ctx) 210 defer cancel() 211 212 taskID, err := agent.ResourceAllocator().AttachNetwork(ctx, containerID, target, addresses) 213 if err != nil { 214 c.mu.Lock() 215 delete(c.attachers, aKey) 216 c.mu.Unlock() 217 return nil, fmt.Errorf("Could not attach to network %s: %v", target, err) 218 } 219 220 c.mu.Lock() 221 c.attachers[aKey].taskID = taskID 222 close(attachCompleteCh) 223 c.mu.Unlock() 224 225 log.G(ctx).Debugf("Successfully attached to network %s with task id %s", target, taskID) 226 227 release := func() { 228 ctx := compatcontext.WithoutCancel(ctx) 229 ctx, cancel := c.getRequestContext(ctx) 230 defer cancel() 231 if err := agent.ResourceAllocator().DetachNetwork(ctx, taskID); err != nil { 232 log.G(ctx).Errorf("Failed remove network attachment %s to network %s on allocation failure: %v", 233 taskID, target, err) 234 } 235 } 236 237 var config *network.NetworkingConfig 238 select { 239 case config = <-attachWaitCh: 240 case <-ctx.Done(): 241 release() 242 return nil, fmt.Errorf("attaching to network failed, make sure your network options are correct and check manager logs: %v", ctx.Err()) 243 } 244 245 c.mu.Lock() 246 c.attachers[aKey].config = config 247 c.mu.Unlock() 248 249 log.G(ctx).Debugf("Successfully allocated resources on network %s for task id %s", target, taskID) 250 251 return config, nil 252 } 253 254 // DetachNetwork unblocks the waiters waiting on WaitForDetachment so 255 // that a request to detach can be generated towards the manager. 256 func (c *Cluster) DetachNetwork(target string, containerID string) error { 257 aKey := attacherKey(target, containerID) 258 259 c.mu.Lock() 260 attacher, ok := c.attachers[aKey] 261 delete(c.attachers, aKey) 262 c.mu.Unlock() 263 264 if !ok { 265 return fmt.Errorf("could not find network attachment for container %s to network %s", containerID, target) 266 } 267 268 close(attacher.detachWaitCh) 269 return nil 270 } 271 272 // CreateNetwork creates a new cluster managed network. 273 func (c *Cluster) CreateNetwork(s apitypes.NetworkCreateRequest) (string, error) { 274 if runconfig.IsPreDefinedNetwork(s.Name) { 275 err := notAllowedError(fmt.Sprintf("%s is a pre-defined network and cannot be created", s.Name)) 276 return "", errors.WithStack(err) 277 } 278 279 var resp *swarmapi.CreateNetworkResponse 280 if err := c.lockedManagerAction(func(ctx context.Context, state nodeState) error { 281 networkSpec := convert.BasicNetworkCreateToGRPC(s) 282 r, err := state.controlClient.CreateNetwork(ctx, &swarmapi.CreateNetworkRequest{Spec: &networkSpec}) 283 if err != nil { 284 return err 285 } 286 resp = r 287 return nil 288 }); err != nil { 289 return "", err 290 } 291 292 return resp.Network.ID, nil 293 } 294 295 // RemoveNetwork removes a cluster network. 296 func (c *Cluster) RemoveNetwork(input string) error { 297 return c.lockedManagerAction(func(ctx context.Context, state nodeState) error { 298 network, err := getNetwork(ctx, state.controlClient, input) 299 if err != nil { 300 return err 301 } 302 303 _, err = state.controlClient.RemoveNetwork(ctx, &swarmapi.RemoveNetworkRequest{NetworkID: network.ID}) 304 return err 305 }) 306 } 307 308 func (c *Cluster) populateNetworkID(ctx context.Context, client swarmapi.ControlClient, s *types.ServiceSpec) error { 309 // Always prefer NetworkAttachmentConfigs from TaskTemplate 310 // but fallback to service spec for backward compatibility 311 networks := s.TaskTemplate.Networks 312 if len(networks) == 0 { 313 networks = s.Networks //nolint:staticcheck // ignore SA1019: field is deprecated. 314 } 315 for i, n := range networks { 316 apiNetwork, err := getNetwork(ctx, client, n.Target) 317 if err != nil { 318 ln, _ := c.config.Backend.FindNetwork(n.Target) 319 if ln != nil && runconfig.IsPreDefinedNetwork(ln.Name()) { 320 // Need to retrieve the corresponding predefined swarm network 321 // and use its id for the request. 322 apiNetwork, err = getNetwork(ctx, client, ln.Name()) 323 if err != nil { 324 return errors.Wrap(errdefs.NotFound(err), "could not find the corresponding predefined swarm network") 325 } 326 goto setid 327 } 328 if ln != nil && !ln.Dynamic() { 329 errMsg := fmt.Sprintf("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()) 330 return errors.WithStack(notAllowedError(errMsg)) 331 } 332 return err 333 } 334 setid: 335 networks[i].Target = apiNetwork.ID 336 } 337 return nil 338 }