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

     1  package serviceregistration
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  
     7  	"github.com/hashicorp/nomad/nomad/structs"
     8  	"github.com/hashicorp/nomad/plugins/drivers"
     9  )
    10  
    11  // GetAddress returns the IP (or custom advertise address) and port to use for a
    12  // service or check registration. If no port label is specified (an empty value)
    13  // and no custom address is specified, zero values are returned because no address
    14  // could be resolved.
    15  func GetAddress(
    16  	address, // custom address, if set
    17  	addressMode,
    18  	portLabel string,
    19  	networks structs.Networks,
    20  	driverNet *drivers.DriverNetwork,
    21  	ports structs.AllocatedPorts,
    22  	netStatus *structs.AllocNetworkStatus,
    23  ) (string, int, error) {
    24  	switch addressMode {
    25  	case structs.AddressModeAuto:
    26  		switch {
    27  		case address != "":
    28  			// No port no problem, just return the advertise address.
    29  			if portLabel == "" {
    30  				return address, 0, nil
    31  			}
    32  			// A custom advertise address can be used with a port map; using the
    33  			// Value and ignoring the IP. The routing from your custom address to
    34  			// the group network address is DIY. (e.g. EC2 public address)
    35  			if mapping, exists := ports.Get(portLabel); exists {
    36  				return address, mapping.Value, nil
    37  			}
    38  			// If not a port map we can interpolate a numeric port for you.
    39  			port, err := strconv.Atoi(portLabel)
    40  			if err != nil {
    41  				return "", 0, fmt.Errorf("invalid port: %q: not a valid port mapping or numeric port", portLabel)
    42  			}
    43  			return address, port, nil
    44  		case driverNet.Advertise():
    45  			return GetAddress("", structs.AddressModeDriver, portLabel, networks, driverNet, ports, netStatus)
    46  		default:
    47  			return GetAddress("", structs.AddressModeHost, portLabel, networks, driverNet, ports, netStatus)
    48  		}
    49  	case structs.AddressModeHost:
    50  		// Cannot use address mode host with custom advertise address.
    51  		if address != "" {
    52  			return "", 0, fmt.Errorf("cannot use custom advertise address with %q address mode", structs.AddressModeHost)
    53  		}
    54  
    55  		if portLabel == "" {
    56  			if len(networks) != 1 {
    57  				// If no networks are specified return zero
    58  				// values. Consul will advertise the host IP
    59  				// with no port. This is the pre-0.7.1 behavior
    60  				// some people rely on.
    61  				return "", 0, nil
    62  			}
    63  			return networks[0].IP, 0, nil
    64  		}
    65  
    66  		// Default path: use host ip:port
    67  		// Try finding port in the AllocatedPorts struct first
    68  		// Check in Networks struct for backwards compatibility if not found
    69  		mapping, ok := ports.Get(portLabel)
    70  		if !ok {
    71  			mapping = networks.Port(portLabel)
    72  			if mapping.Value > 0 {
    73  				return mapping.HostIP, mapping.Value, nil
    74  			}
    75  
    76  			// If port isn't a label, try to parse it as a literal port number
    77  			port, err := strconv.Atoi(portLabel)
    78  			if err != nil {
    79  				// Don't include Atoi error message as user likely
    80  				// never intended it to be a numeric and it creates a
    81  				// confusing error message
    82  				return "", 0, fmt.Errorf("invalid port %q: port label not found", portLabel)
    83  			}
    84  			if port <= 0 {
    85  				return "", 0, fmt.Errorf("invalid port: %q: port must be >0", portLabel)
    86  			}
    87  
    88  			// A number was given which will use the Consul agent's address and the given port
    89  			// Returning a blank string as an address will use the Consul agent's address
    90  			return "", port, nil
    91  		}
    92  		return mapping.HostIP, mapping.Value, nil
    93  
    94  	case structs.AddressModeDriver:
    95  		// Cannot use address mode driver with custom advertise address.
    96  		if address != "" {
    97  			return "", 0, fmt.Errorf("cannot use custom advertise address with %q address mode", structs.AddressModeDriver)
    98  		}
    99  
   100  		// Require a driver network if driver address mode is used
   101  		if driverNet == nil {
   102  			return "", 0, fmt.Errorf(`cannot use address_mode="driver": no driver network exists`)
   103  		}
   104  
   105  		// If no port label is specified just return the IP
   106  		if portLabel == "" {
   107  			return driverNet.IP, 0, nil
   108  		}
   109  
   110  		// If the port is a label, use the driver's port (not the host's)
   111  		if port, ok := ports.Get(portLabel); ok {
   112  			return driverNet.IP, port.To, nil
   113  		}
   114  
   115  		// Check if old style driver portmap is used
   116  		if port, ok := driverNet.PortMap[portLabel]; ok {
   117  			return driverNet.IP, port, nil
   118  		}
   119  
   120  		// If port isn't a label, try to parse it as a literal port number
   121  		port, err := strconv.Atoi(portLabel)
   122  		if err != nil {
   123  			// Don't include Atoi error message as user likely
   124  			// never intended it to be a numeric and it creates a
   125  			// confusing error message
   126  			return "", 0, fmt.Errorf("invalid port label %q: port labels in driver address_mode must be numeric or in the driver's port map", portLabel)
   127  		}
   128  		if port <= 0 {
   129  			return "", 0, fmt.Errorf("invalid port: %q: port must be >0", portLabel)
   130  		}
   131  
   132  		return driverNet.IP, port, nil
   133  
   134  	case structs.AddressModeAlloc:
   135  		// Cannot use address mode alloc with custom advertise address.
   136  		if address != "" {
   137  			return "", 0, fmt.Errorf("cannot use custom advertise address with %q address mode", structs.AddressModeAlloc)
   138  		}
   139  
   140  		// Going to need a network for this.
   141  		if netStatus == nil {
   142  			return "", 0, fmt.Errorf(`cannot use address_mode="alloc": no allocation network status reported`)
   143  		}
   144  
   145  		// If no port label is specified just return the IP
   146  		if portLabel == "" {
   147  			return netStatus.Address, 0, nil
   148  		}
   149  
   150  		// If port is a label and is found then return it
   151  		if port, ok := ports.Get(portLabel); ok {
   152  			// Use port.To value unless not set
   153  			if port.To > 0 {
   154  				return netStatus.Address, port.To, nil
   155  			}
   156  			return netStatus.Address, port.Value, nil
   157  		}
   158  
   159  		// Check if port is a literal number
   160  		port, err := strconv.Atoi(portLabel)
   161  		if err != nil {
   162  			// User likely specified wrong port label here
   163  			return "", 0, fmt.Errorf("invalid port %q: port label not found or is not numeric", portLabel)
   164  		}
   165  		if port <= 0 {
   166  			return "", 0, fmt.Errorf("invalid port: %q: port must be >0", portLabel)
   167  		}
   168  		return netStatus.Address, port, nil
   169  
   170  	default:
   171  		// Shouldn't happen due to validation, but enforce invariants
   172  		return "", 0, fmt.Errorf("invalid address mode %q", addressMode)
   173  	}
   174  }