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  }