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 }