github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/allocrunner/network_manager_linux.go (about)

     1  package allocrunner
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path"
     7  	"strings"
     8  	"syscall"
     9  
    10  	hclog "github.com/hashicorp/go-hclog"
    11  	clientconfig "github.com/hashicorp/nomad/client/config"
    12  	"github.com/hashicorp/nomad/client/lib/nsutil"
    13  	"github.com/hashicorp/nomad/client/pluginmanager/drivermanager"
    14  	"github.com/hashicorp/nomad/nomad/structs"
    15  	"github.com/hashicorp/nomad/plugins/drivers"
    16  )
    17  
    18  func newNetworkManager(alloc *structs.Allocation, driverManager drivermanager.Manager) (nm drivers.DriverNetworkManager, err error) {
    19  	// The defaultNetworkManager is used if a driver doesn't need to create the network
    20  	nm = &defaultNetworkManager{}
    21  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
    22  
    23  	// default netmode to host, this can be overridden by the task or task group
    24  	tgNetMode := "host"
    25  	if len(tg.Networks) > 0 && tg.Networks[0].Mode != "" {
    26  		tgNetMode = tg.Networks[0].Mode
    27  	}
    28  
    29  	groupIsolationMode := netModeToIsolationMode(tgNetMode)
    30  
    31  	// Setting the hostname is only possible where the task groups networking
    32  	// mode is group; meaning bridge or none.
    33  	if len(tg.Networks) > 0 &&
    34  		(groupIsolationMode != drivers.NetIsolationModeGroup && tg.Networks[0].Hostname != "") {
    35  		return nil, fmt.Errorf("hostname cannot be set on task group using %q networking mode",
    36  			groupIsolationMode)
    37  	}
    38  
    39  	// networkInitiator tracks the task driver which needs to create the network
    40  	// to check for multiple drivers needing to create the network.
    41  	var networkInitiator string
    42  
    43  	// driverCaps tracks which drivers we've checked capabilities for so as not
    44  	// to do extra work
    45  	driverCaps := make(map[string]struct{})
    46  	for _, task := range tg.Tasks {
    47  		// the task's netmode defaults to the the task group but can be overridden
    48  		taskNetMode := tgNetMode
    49  		if len(task.Resources.Networks) > 0 && task.Resources.Networks[0].Mode != "" {
    50  			taskNetMode = task.Resources.Networks[0].Mode
    51  		}
    52  
    53  		// netmode host should always work to support backwards compat
    54  		if taskNetMode == "host" {
    55  			continue
    56  		}
    57  
    58  		// check to see if capabilities of this task's driver have already been checked
    59  		if _, ok := driverCaps[task.Driver]; ok {
    60  			continue
    61  		}
    62  
    63  		driver, err := driverManager.Dispense(task.Driver)
    64  		if err != nil {
    65  			return nil, fmt.Errorf("failed to dispense driver %s: %v", task.Driver, err)
    66  		}
    67  
    68  		caps, err := driver.Capabilities()
    69  		if err != nil {
    70  			return nil, fmt.Errorf("failed to retrieve capabilities for driver %s: %v",
    71  				task.Driver, err)
    72  		}
    73  
    74  		// check that the driver supports the requested network isolation mode
    75  		netIsolationMode := netModeToIsolationMode(taskNetMode)
    76  		if !caps.HasNetIsolationMode(netIsolationMode) {
    77  			return nil, fmt.Errorf("task %s does not support %q networking mode", task.Name, taskNetMode)
    78  		}
    79  
    80  		// check if the driver needs to create the network and if a different
    81  		// driver has already claimed it needs to initiate the network
    82  		if caps.MustInitiateNetwork {
    83  			if networkInitiator != "" {
    84  				return nil, fmt.Errorf("tasks %s and %s want to initiate networking but only one driver can do so", networkInitiator, task.Name)
    85  			}
    86  			netManager, ok := driver.(drivers.DriverNetworkManager)
    87  			if !ok {
    88  				return nil, fmt.Errorf("driver %s does not implement network management RPCs", task.Driver)
    89  			}
    90  
    91  			nm = netManager
    92  			networkInitiator = task.Name
    93  		} else if tg.Networks[0].Hostname != "" {
    94  			// TODO jrasell: remove once the default linux network manager
    95  			//  supports setting the hostname in bridged mode. This currently
    96  			//  indicates only Docker supports this, which is true unless a
    97  			//  custom driver can which means this check still holds as true as
    98  			//  we can tell.
    99  			//  Please see: https://github.com/hashicorp/nomad/issues/11180
   100  			return nil, fmt.Errorf("hostname is not currently supported on driver %s", task.Driver)
   101  		}
   102  
   103  		// mark this driver's capabilities as checked
   104  		driverCaps[task.Driver] = struct{}{}
   105  	}
   106  
   107  	return nm, nil
   108  }
   109  
   110  // defaultNetworkManager creates a network namespace for the alloc
   111  type defaultNetworkManager struct{}
   112  
   113  // CreateNetwork is the CreateNetwork implementation of the
   114  // drivers.DriverNetworkManager interface function. It does not currently
   115  // support setting the hostname of the network namespace.
   116  func (*defaultNetworkManager) CreateNetwork(allocID string, _ *drivers.NetworkCreateRequest) (*drivers.NetworkIsolationSpec, bool, error) {
   117  	netns, err := nsutil.NewNS(allocID)
   118  	if err != nil {
   119  		// when a client restarts, the namespace will already exist and
   120  		// there will be a namespace file in use by the task process
   121  		if e, ok := err.(*os.PathError); ok && e.Err == syscall.EPERM {
   122  			nsPath := path.Join(nsutil.NetNSRunDir, allocID)
   123  			_, err := os.Stat(nsPath)
   124  			if err == nil {
   125  				// Let's return a spec that points to the tested nspath, but indicate
   126  				// that we didn't make the namespace. That will stop the network_hook
   127  				// from calling its networkConfigurator.Setup function in the reconnect
   128  				// case, but provide the spec value necessary for the network_hook's
   129  				// Postrun function to not fast exit.
   130  				spec := &drivers.NetworkIsolationSpec{
   131  					Mode:   drivers.NetIsolationModeGroup,
   132  					Path:   nsPath,
   133  					Labels: make(map[string]string),
   134  				}
   135  
   136  				return spec, false, nil
   137  			}
   138  		}
   139  		return nil, false, err
   140  	}
   141  
   142  	spec := &drivers.NetworkIsolationSpec{
   143  		Mode:   drivers.NetIsolationModeGroup,
   144  		Path:   netns.Path(),
   145  		Labels: make(map[string]string),
   146  	}
   147  
   148  	return spec, true, nil
   149  }
   150  
   151  func (*defaultNetworkManager) DestroyNetwork(allocID string, spec *drivers.NetworkIsolationSpec) error {
   152  	return nsutil.UnmountNS(spec.Path)
   153  }
   154  
   155  func netModeToIsolationMode(netMode string) drivers.NetIsolationMode {
   156  	switch strings.ToLower(netMode) {
   157  	case "host":
   158  		return drivers.NetIsolationModeHost
   159  	case "bridge", "none":
   160  		return drivers.NetIsolationModeGroup
   161  	case "driver":
   162  		return drivers.NetIsolationModeTask
   163  	default:
   164  		if strings.HasPrefix(strings.ToLower(netMode), "cni/") {
   165  			return drivers.NetIsolationModeGroup
   166  		}
   167  		return drivers.NetIsolationModeHost
   168  	}
   169  }
   170  
   171  func newNetworkConfigurator(log hclog.Logger, alloc *structs.Allocation, config *clientconfig.Config) (NetworkConfigurator, error) {
   172  	tg := alloc.Job.LookupTaskGroup(alloc.TaskGroup)
   173  
   174  	// Check if network stanza is given
   175  	if len(tg.Networks) == 0 {
   176  		return &hostNetworkConfigurator{}, nil
   177  	}
   178  
   179  	netMode := strings.ToLower(tg.Networks[0].Mode)
   180  	ignorePortMappingHostIP := config.BindWildcardDefaultHostNetwork
   181  	if len(config.HostNetworks) > 0 {
   182  		ignorePortMappingHostIP = false
   183  	}
   184  
   185  	switch {
   186  	case netMode == "bridge":
   187  		c, err := newBridgeNetworkConfigurator(log, config.BridgeNetworkName, config.BridgeNetworkAllocSubnet, config.CNIPath, ignorePortMappingHostIP)
   188  		if err != nil {
   189  			return nil, err
   190  		}
   191  		return &synchronizedNetworkConfigurator{c}, nil
   192  	case strings.HasPrefix(netMode, "cni/"):
   193  		c, err := newCNINetworkConfigurator(log, config.CNIPath, config.CNIInterfacePrefix, config.CNIConfigDir, netMode[4:], ignorePortMappingHostIP)
   194  		if err != nil {
   195  			return nil, err
   196  		}
   197  		return &synchronizedNetworkConfigurator{c}, nil
   198  	default:
   199  		return &hostNetworkConfigurator{}, nil
   200  	}
   201  }