github.com/containerd/nerdctl@v1.7.7/pkg/containerutil/container_network_manager_windows.go (about)

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package containerutil
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/containerd/containerd"
    24  	"github.com/containerd/containerd/oci"
    25  	"github.com/containerd/containerd/pkg/netns"
    26  	gocni "github.com/containerd/go-cni"
    27  
    28  	"github.com/containerd/nerdctl/pkg/api/types"
    29  	"github.com/containerd/nerdctl/pkg/netutil"
    30  	"github.com/containerd/nerdctl/pkg/ocihook"
    31  )
    32  
    33  type cniNetworkManagerPlatform struct {
    34  	netNs *netns.NetNS
    35  }
    36  
    37  // Verifies that the internal network settings are correct.
    38  func (m *cniNetworkManager) VerifyNetworkOptions(_ context.Context) error {
    39  	e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	// NOTE: only currently supported network type on Windows is nat:
    45  	validNetworkTypes := []string{"nat"}
    46  	if _, err := verifyNetworkTypes(e, m.netOpts.NetworkSlice, validNetworkTypes); err != nil {
    47  		return err
    48  	}
    49  
    50  	nonZeroArgs := nonZeroMapValues(map[string]interface{}{
    51  		"--hostname": m.netOpts.Hostname,
    52  		"--uts":      m.netOpts.UTSNamespace,
    53  		// NOTE: IP and MAC settings are currently ignored on Windows.
    54  		"--ip-address":  m.netOpts.IPAddress,
    55  		"--mac-address": m.netOpts.MACAddress,
    56  		// NOTE: zero-length slices count as a non-zero-value so we explicitly check length:
    57  		"--dns-opt/--dns-option": len(m.netOpts.DNSResolvConfOptions) != 0,
    58  		"--dns-servers":          len(m.netOpts.DNSServers) != 0,
    59  		"--dns-search":           len(m.netOpts.DNSSearchDomains) != 0,
    60  		"--add-host":             len(m.netOpts.AddHost) != 0,
    61  	})
    62  	if len(nonZeroArgs) != 0 {
    63  		return fmt.Errorf("the following networking arguments are not supported on Windows: %+v", nonZeroArgs)
    64  	}
    65  
    66  	return nil
    67  }
    68  
    69  func (m *cniNetworkManager) getCNI() (gocni.CNI, error) {
    70  	e, err := netutil.NewCNIEnv(m.globalOptions.CNIPath, m.globalOptions.CNINetConfPath, netutil.WithDefaultNetwork())
    71  	if err != nil {
    72  		return nil, fmt.Errorf("failed to instantiate CNI env: %s", err)
    73  	}
    74  
    75  	cniOpts := []gocni.Opt{
    76  		gocni.WithPluginDir([]string{m.globalOptions.CNIPath}),
    77  		gocni.WithPluginConfDir(m.globalOptions.CNINetConfPath),
    78  	}
    79  
    80  	if netMap, err := verifyNetworkTypes(e, m.netOpts.NetworkSlice, nil); err == nil {
    81  		for _, netConf := range netMap {
    82  			cniOpts = append(cniOpts, gocni.WithConfListBytes(netConf.Bytes))
    83  		}
    84  	} else {
    85  		return nil, err
    86  	}
    87  
    88  	return gocni.New(cniOpts...)
    89  }
    90  
    91  // Performs setup actions required for the container with the given ID.
    92  func (m *cniNetworkManager) SetupNetworking(ctx context.Context, containerID string) error {
    93  	cni, err := m.getCNI()
    94  	if err != nil {
    95  		return fmt.Errorf("failed to get container networking for setup: %s", err)
    96  	}
    97  
    98  	netNs, err := m.setupNetNs()
    99  	if err != nil {
   100  		return err
   101  	}
   102  
   103  	_, err = cni.Setup(ctx, containerID, netNs.GetPath(), m.getCNINamespaceOpts()...)
   104  	return err
   105  }
   106  
   107  // Performs any required cleanup actions for the given container.
   108  // Should only be called to revert any setup steps performed in setupNetworking.
   109  func (m *cniNetworkManager) CleanupNetworking(ctx context.Context, container containerd.Container) error {
   110  	containerID := container.ID()
   111  	cni, err := m.getCNI()
   112  	if err != nil {
   113  		return fmt.Errorf("failed to get container networking for cleanup: %s", err)
   114  	}
   115  
   116  	spec, err := container.Spec(ctx)
   117  	if err != nil {
   118  		return fmt.Errorf("failed to get container specs for networking cleanup: %s", err)
   119  	}
   120  
   121  	netNsId, found := spec.Annotations[ocihook.NetworkNamespace]
   122  	if !found {
   123  		return fmt.Errorf("no %q annotation present on container with ID %s", ocihook.NetworkNamespace, containerID)
   124  	}
   125  
   126  	return cni.Remove(ctx, containerID, netNsId, m.getCNINamespaceOpts()...)
   127  }
   128  
   129  // Returns the set of NetworkingOptions which should be set as labels on the container.
   130  func (m *cniNetworkManager) InternalNetworkingOptionLabels(_ context.Context) (types.NetworkOptions, error) {
   131  	return m.netOpts, nil
   132  }
   133  
   134  // Returns a slice of `oci.SpecOpts` and `containerd.NewContainerOpts` which represent
   135  // the network specs which need to be applied to the container with the given ID.
   136  func (m *cniNetworkManager) ContainerNetworkingOpts(_ context.Context, containerID string) ([]oci.SpecOpts, []containerd.NewContainerOpts, error) {
   137  	ns, err := m.setupNetNs()
   138  	if err != nil {
   139  		return nil, nil, err
   140  	}
   141  
   142  	opts := []oci.SpecOpts{
   143  		oci.WithWindowNetworksAllowUnqualifiedDNSQuery(),
   144  		oci.WithWindowsNetworkNamespace(ns.GetPath()),
   145  	}
   146  
   147  	cOpts := []containerd.NewContainerOpts{
   148  		containerd.WithAdditionalContainerLabels(
   149  			map[string]string{
   150  				ocihook.NetworkNamespace: ns.GetPath(),
   151  			},
   152  		),
   153  	}
   154  
   155  	return opts, cOpts, nil
   156  }
   157  
   158  // Returns the string path to a network namespace.
   159  func (m *cniNetworkManager) setupNetNs() (*netns.NetNS, error) {
   160  	if m.netNs != nil {
   161  		return m.netNs, nil
   162  	}
   163  
   164  	// NOTE: the baseDir argument to NewNetNS is ignored on Windows.
   165  	ns, err := netns.NewNetNS("")
   166  	if err != nil {
   167  		return nil, err
   168  	}
   169  
   170  	m.netNs = ns
   171  	return ns, err
   172  }
   173  
   174  // Returns the []gocni.NamespaceOpts to be used for CNI setup/teardown.
   175  func (m *cniNetworkManager) getCNINamespaceOpts() []gocni.NamespaceOpts {
   176  	opts := []gocni.NamespaceOpts{
   177  		gocni.WithLabels(map[string]string{
   178  			// allow loose CNI argument verification
   179  			// FYI: https://github.com/containernetworking/cni/issues/560
   180  			"IgnoreUnknown": "1",
   181  		}),
   182  	}
   183  
   184  	if m.netOpts.MACAddress != "" {
   185  		opts = append(opts, gocni.WithArgs("MAC", m.netOpts.MACAddress))
   186  	}
   187  
   188  	if m.netOpts.IPAddress != "" {
   189  		opts = append(opts, gocni.WithArgs("IP", m.netOpts.IPAddress))
   190  	}
   191  
   192  	if m.netOpts.PortMappings != nil {
   193  		opts = append(opts, gocni.WithCapabilityPortMap(m.netOpts.PortMappings))
   194  	}
   195  
   196  	return opts
   197  }