diff --git a/command/agent/config.go b/command/agent/config.go index cccd095f1010..279301110897 100644 --- a/command/agent/config.go +++ b/command/agent/config.go @@ -2,6 +2,7 @@ package agent import ( "encoding/base64" + "errors" "fmt" "io" "net" @@ -13,6 +14,8 @@ import ( "strings" "time" + "github.com/hashicorp/go-sockaddr/template" + client "github.com/hashicorp/nomad/client/config" "github.com/hashicorp/nomad/helper" "github.com/hashicorp/nomad/nomad" @@ -716,16 +719,39 @@ func (c *Config) Merge(b *Config) *Config { // normalizeAddrs normalizes Addresses and AdvertiseAddrs to always be // initialized and have sane defaults. func (c *Config) normalizeAddrs() error { - c.Addresses.HTTP = normalizeBind(c.Addresses.HTTP, c.BindAddr) - c.Addresses.RPC = normalizeBind(c.Addresses.RPC, c.BindAddr) - c.Addresses.Serf = normalizeBind(c.Addresses.Serf, c.BindAddr) + if c.BindAddr != "" { + ipStr, err := parseSingleIPTemplate(c.BindAddr) + if err != nil { + return fmt.Errorf("Bind address resolution failed: %v", err) + } + c.BindAddr = ipStr + } + + addr, err := normalizeBind(c.Addresses.HTTP, c.BindAddr) + if err != nil { + return fmt.Errorf("Failed to parse HTTP address: %v", err) + } + c.Addresses.HTTP = addr + + addr, err = normalizeBind(c.Addresses.RPC, c.BindAddr) + if err != nil { + return fmt.Errorf("Failed to parse RPC address: %v", err) + } + c.Addresses.RPC = addr + + addr, err = normalizeBind(c.Addresses.Serf, c.BindAddr) + if err != nil { + return fmt.Errorf("Failed to parse Serf address: %v", err) + } + c.Addresses.Serf = addr + c.normalizedAddrs = &Addresses{ HTTP: net.JoinHostPort(c.Addresses.HTTP, strconv.Itoa(c.Ports.HTTP)), RPC: net.JoinHostPort(c.Addresses.RPC, strconv.Itoa(c.Ports.RPC)), Serf: net.JoinHostPort(c.Addresses.Serf, strconv.Itoa(c.Ports.Serf)), } - addr, err := normalizeAdvertise(c.AdvertiseAddrs.HTTP, c.Addresses.HTTP, c.Ports.HTTP, c.DevMode) + addr, err = normalizeAdvertise(c.AdvertiseAddrs.HTTP, c.Addresses.HTTP, c.Ports.HTTP, c.DevMode) if err != nil { return fmt.Errorf("Failed to parse HTTP advertise address: %v", err) } @@ -749,14 +775,34 @@ func (c *Config) normalizeAddrs() error { return nil } +// parseSingleIPTemplate is used as a helper function to parse out a single IP +// address from a config parameter. +func parseSingleIPTemplate(ipTmpl string) (string, error) { + out, err := template.Parse(ipTmpl) + if err != nil { + return "", fmt.Errorf("Unable to parse address template %q: %v", ipTmpl, err) + } + + ips := strings.Split(out, " ") + switch len(ips) { + case 0: + return "", errors.New("No addresses found, please configure one.") + case 1: + return ips[0], nil + default: + return "", fmt.Errorf("Multiple addresses found (%q), please configure one.", out) + } +} + // normalizeBind returns a normalized bind address. // // If addr is set it is used, if not the default bind address is used. -func normalizeBind(addr, bind string) string { +func normalizeBind(addr, bind string) (string, error) { if addr == "" { - return bind + return bind, nil + } else { + return parseSingleIPTemplate(addr) } - return addr } // normalizeAdvertise returns a normalized advertise address. @@ -772,18 +818,23 @@ func normalizeBind(addr, bind string) string { // // Loopback is only considered a valid advertise address in dev mode. func normalizeAdvertise(addr string, bind string, defport int, dev bool) (string, error) { + addr, err := parseSingleIPTemplate(addr) + if err != nil { + return "", fmt.Errorf("Error parsing advertise address template: %v", err) + } + if addr != "" { // Default to using manually configured address - _, _, err := net.SplitHostPort(addr) + host, port, err := net.SplitHostPort(addr) if err != nil { if !isMissingPort(err) { return "", fmt.Errorf("Error parsing advertise address %q: %v", addr, err) } - - // missing port, append the default - return net.JoinHostPort(addr, strconv.Itoa(defport)), nil + host = addr + port = strconv.Itoa(defport) } - return addr, nil + + return net.JoinHostPort(host, port), nil } // Fallback to bind address first, and then try resolving the local hostname @@ -797,35 +848,18 @@ func normalizeAdvertise(addr string, bind string, defport int, dev bool) (string if ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast() { return net.JoinHostPort(ip.String(), strconv.Itoa(defport)), nil } - if ip.IsLoopback() && dev { + if !ip.IsLoopback() || (ip.IsLoopback() && dev) { // loopback is fine for dev mode return net.JoinHostPort(ip.String(), strconv.Itoa(defport)), nil } } - // As a last resort resolve the hostname and use it if it's not - // localhost (as localhost is never a sensible default) - host, err := os.Hostname() + // Otherwise, default to the private IP address + addr, err = parseSingleIPTemplate("{{ GetPrivateIP }}") if err != nil { - return "", fmt.Errorf("Unable to get hostname to set advertise address: %v", err) - } - - ips, err = net.LookupIP(host) - if err != nil { - return "", fmt.Errorf("Error resolving hostname %q for advertise address: %v", host, err) - } - - // Return the first unicast address - for _, ip := range ips { - if ip.IsLinkLocalUnicast() || ip.IsGlobalUnicast() { - return net.JoinHostPort(ip.String(), strconv.Itoa(defport)), nil - } - if ip.IsLoopback() && dev { - // loopback is fine for dev mode - return net.JoinHostPort(ip.String(), strconv.Itoa(defport)), nil - } + return "", fmt.Errorf("Unable to parse default advertise address: %v", err) } - return "", fmt.Errorf("No valid advertise addresses, please set `advertise` manually") + return net.JoinHostPort(addr, strconv.Itoa(defport)), nil } // isMissingPort returns true if an error is a "missing port" error from diff --git a/command/agent/config_test.go b/command/agent/config_test.go index d28ef336f1b6..270ffe23b62e 100644 --- a/command/agent/config_test.go +++ b/command/agent/config_test.go @@ -1,6 +1,7 @@ package agent import ( + "fmt" "io/ioutil" "net" "os" @@ -523,6 +524,189 @@ func TestConfig_Listener(t *testing.T) { } } +func TestConfig_normalizeAddrs(t *testing.T) { + // allow to advertise 127.0.0.1 if dev-mode is enabled + c := &Config{ + BindAddr: "127.0.0.1", + Ports: &Ports{ + HTTP: 4646, + RPC: 4647, + Serf: 4648, + }, + Addresses: &Addresses{}, + AdvertiseAddrs: &AdvertiseAddrs{}, + DevMode: true, + } + + if err := c.normalizeAddrs(); err != nil { + t.Fatalf("unable to normalize addresses: %s", err) + } + + if c.BindAddr != "127.0.0.1" { + t.Fatalf("expected BindAddr 127.0.0.1, got %s", c.BindAddr) + } + + if c.normalizedAddrs.HTTP != "127.0.0.1:4646" { + t.Fatalf("expected HTTP address 127.0.0.1:4646, got %s", c.normalizedAddrs.HTTP) + } + + if c.normalizedAddrs.RPC != "127.0.0.1:4647" { + t.Fatalf("expected RPC address 127.0.0.1:4647, got %s", c.normalizedAddrs.RPC) + } + + if c.normalizedAddrs.Serf != "127.0.0.1:4648" { + t.Fatalf("expected Serf address 127.0.0.1:4648, got %s", c.normalizedAddrs.Serf) + } + + if c.AdvertiseAddrs.HTTP != "127.0.0.1:4646" { + t.Fatalf("expected HTTP advertise address 127.0.0.1:4646, got %s", c.AdvertiseAddrs.HTTP) + } + + if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { + t.Fatalf("expected RPC advertise address 127.0.0.1:4647, got %s", c.AdvertiseAddrs.RPC) + } + + // Client mode, no Serf address defined + if c.AdvertiseAddrs.Serf != "" { + t.Fatalf("expected unset Serf advertise address, got %s", c.AdvertiseAddrs.Serf) + } + + // default to non-localhost address in non-dev mode + c = &Config{ + BindAddr: "127.0.0.1", + Ports: &Ports{ + HTTP: 4646, + RPC: 4647, + Serf: 4648, + }, + Addresses: &Addresses{}, + AdvertiseAddrs: &AdvertiseAddrs{}, + DevMode: false, + } + + if err := c.normalizeAddrs(); err != nil { + t.Fatalf("unable to normalize addresses: %s", err) + } + + if c.AdvertiseAddrs.HTTP == "127.0.0.1:4646" { + t.Fatalf("expected non-localhost HTTP advertise address, got %s", c.AdvertiseAddrs.HTTP) + } + + if c.AdvertiseAddrs.RPC == "127.0.0.1:4647" { + t.Fatalf("expected non-localhost RPC advertise address, got %s", c.AdvertiseAddrs.RPC) + } + + if c.AdvertiseAddrs.Serf == "127.0.0.1:4648" { + t.Fatalf("expected non-localhost Serf advertise address, got %s", c.AdvertiseAddrs.Serf) + } + + c = &Config{ + BindAddr: "169.254.1.5", + Ports: &Ports{ + HTTP: 4646, + RPC: 4647, + Serf: 4648, + }, + Addresses: &Addresses{ + HTTP: "169.254.1.10", + }, + AdvertiseAddrs: &AdvertiseAddrs{ + RPC: "169.254.1.40", + }, + Server: &ServerConfig{ + Enabled: true, + }, + } + + if err := c.normalizeAddrs(); err != nil { + t.Fatalf("unable to normalize addresses: %s", err) + } + + if c.BindAddr != "169.254.1.5" { + t.Fatalf("expected BindAddr 169.254.1.5, got %s", c.BindAddr) + } + + if c.AdvertiseAddrs.HTTP != "169.254.1.10:4646" { + t.Fatalf("expected HTTP advertise address 169.254.1.10:4646, got %s", c.AdvertiseAddrs.HTTP) + } + + if c.AdvertiseAddrs.RPC != "169.254.1.40:4647" { + t.Fatalf("expected RPC advertise address 169.254.1.40:4647, got %s", c.AdvertiseAddrs.RPC) + } + + if c.AdvertiseAddrs.Serf != "169.254.1.5:4648" { + t.Fatalf("expected Serf advertise address 169.254.1.5:4648, got %s", c.AdvertiseAddrs.Serf) + } + + c = &Config{ + BindAddr: "{{ GetPrivateIP }}", + Ports: &Ports{ + HTTP: 4646, + RPC: 4647, + Serf: 4648, + }, + Addresses: &Addresses{}, + AdvertiseAddrs: &AdvertiseAddrs{ + RPC: "{{ GetPrivateIP }}:8888", + }, + Server: &ServerConfig{ + Enabled: true, + }, + } + + if err := c.normalizeAddrs(); err != nil { + t.Fatalf("unable to normalize addresses: %s", err) + } + + if c.AdvertiseAddrs.HTTP != fmt.Sprintf("%s:4646", c.BindAddr) { + t.Fatalf("expected HTTP advertise address %s:4646, got %s", c.BindAddr, c.AdvertiseAddrs.HTTP) + } + + if c.AdvertiseAddrs.RPC != fmt.Sprintf("%s:8888", c.BindAddr) { + t.Fatalf("expected RPC advertise address %s:8888, got %s", c.BindAddr, c.AdvertiseAddrs.RPC) + } + + if c.AdvertiseAddrs.Serf != fmt.Sprintf("%s:4648", c.BindAddr) { + t.Fatalf("expected Serf advertise address %s:4648, got %s", c.BindAddr, c.AdvertiseAddrs.Serf) + } + + // allow to advertise 127.0.0.1 in non-dev mode, if explicitly configured to do so + c = &Config{ + BindAddr: "127.0.0.1", + Ports: &Ports{ + HTTP: 4646, + RPC: 4647, + Serf: 4648, + }, + Addresses: &Addresses{}, + AdvertiseAddrs: &AdvertiseAddrs{ + HTTP: "127.0.0.1:4646", + RPC: "127.0.0.1:4647", + Serf: "127.0.0.1:4648", + }, + DevMode: false, + Server: &ServerConfig{ + Enabled: true, + }, + } + + if err := c.normalizeAddrs(); err != nil { + t.Fatalf("unable to normalize addresses: %s", err) + } + + if c.AdvertiseAddrs.HTTP != "127.0.0.1:4646" { + t.Fatalf("expected HTTP advertise address 127.0.0.1:4646, got %s", c.AdvertiseAddrs.HTTP) + } + + if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { + t.Fatalf("expected RPC advertise address 127.0.0.1:4647, got %s", c.AdvertiseAddrs.RPC) + } + + if c.AdvertiseAddrs.RPC != "127.0.0.1:4647" { + t.Fatalf("expected RPC advertise address 127.0.0.1:4647, got %s", c.AdvertiseAddrs.RPC) + } +} + func TestResources_ParseReserved(t *testing.T) { cases := []struct { Input string diff --git a/vendor/github.com/hashicorp/go-sockaddr/template/Makefile b/vendor/github.com/hashicorp/go-sockaddr/template/Makefile new file mode 100644 index 000000000000..ce1e274e47a4 --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/template/Makefile @@ -0,0 +1,2 @@ +test:: + go test diff --git a/vendor/github.com/hashicorp/go-sockaddr/template/README.md b/vendor/github.com/hashicorp/go-sockaddr/template/README.md new file mode 100644 index 000000000000..c40905af72ca --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/template/README.md @@ -0,0 +1,6 @@ +# sockaddr/template + +sockaddr's template library. See +the +[sockaddr/template](https://godoc.org/github.com/hashicorp/go-sockaddr/template) +docs for details on how to use this template. diff --git a/vendor/github.com/hashicorp/go-sockaddr/template/doc.go b/vendor/github.com/hashicorp/go-sockaddr/template/doc.go new file mode 100644 index 000000000000..59945d7bef5d --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/template/doc.go @@ -0,0 +1,239 @@ +/* + +Package sockaddr/template provides a text/template interface the SockAddr helper +functions. The primary entry point into the sockaddr/template package is +through its Parse() call. For example: + + import ( + "fmt" + + template "github.com/hashicorp/go-sockaddr/template" + ) + + results, err := template.Parse(`{{ GetPrivateIP }}`) + if err != nil { + fmt.Errorf("Unable to find a private IP address: %v", err) + } + fmt.Printf("My Private IP address is: %s\n", results) + +Below is a list of builtin template functions and details re: their usage. It +is possible to add additional functions by calling ParseIfAddrsTemplate +directly. + +In general, the calling convention for this template library is to seed a list +of initial interfaces via one of the Get*Interfaces() calls, then filter, sort, +and extract the necessary attributes for use as string input. This template +interface is primarily geared toward resolving specific values that are only +available at runtime, but can be defined as a heuristic for execution when a +config file is parsed. + +All functions, unless noted otherwise, return an array of IfAddr structs making +it possible to `sort`, `filter`, `limit`, seek (via the `offset` function), or +`unique` the list. To extract useful string information, the `attr` and `join` +functions return a single string value. See below for details. + +Important note: see the +https://github.com/hashicorp/go-sockaddr/tree/master/cmd/sockaddr utility for +more examples and for a CLI utility to experiment with the template syntax. + +`GetAllInterfaces` - Returns an exhaustive set of IfAddr structs available on +the host. `GetAllInterfaces` is the initial input and accessible as the initial +"dot" in the pipeline. + +Example: + + {{ GetAllInterfaces }} + + +`GetDefaultInterfaces` - Returns one IfAddr for every IP that is on the +interface containing the default route for the host. + +Example: + + {{ GetDefaultInterfaces }} + +`GetPrivateInterfaces` - Returns one IfAddr for every forwardable IP address +that is included in RFC 6890, is attached to the interface with the default +route, and whose interface is marked as up. NOTE: RFC 6890 is a more exhaustive +version of RFC1918 because it spans IPv4 and IPv6, however it does permit the +inclusion of likely undesired addresses such as multicast, therefore our version +of "private" also filters out non-forwardable addresses. + +Example: + + {{ GetPrivateInterfaces | include "flags" "up" }} + + +`GetPublicInterfaces` - Returns a list of IfAddr that do not match RFC 6890, is +attached to the default route, and whose interface is marked as up. + +Example: + + {{ GetPublicInterfaces | include "flags" "up" }} + + +`GetPrivateIP` - Helper function that returns a string of the first IP address +from GetPrivateInterfaces. + +Example: + + {{ GetPrivateIP }} + + +`GetPublicIP` - Helper function that returns a string of the first IP from +GetPublicInterfaces. + +Example: + + {{ GetPublicIP }} + +`GetInterfaceIP` - Helper function that returns a string of the first IP from +the named interface. + +Example: + + {{ GetInterfaceIP }} + + +`sort` - Sorts the IfAddrs result based on its arguments. `sort` takes one +argument, a list of ways to sort its IfAddrs argument. The list of sort +criteria is comma separated (`,`): + - `address`, `+address`: Ascending sort of IfAddrs by Address + - `-address`: Descending sort of IfAddrs by Address + - `name`, `+name`: Ascending sort of IfAddrs by lexical ordering of interface name + - `-name`: Descending sort of IfAddrs by lexical ordering of interface name + - `port`, `+port`: Ascending sort of IfAddrs by port number + - `-port`: Descending sort of IfAddrs by port number + - `private`, `+private`: Ascending sort of IfAddrs with private addresses first + - `-private`: Descending sort IfAddrs with private addresses last + - `size`, `+size`: Ascending sort of IfAddrs by their network size as determined + by their netmask (larger networks first) + - `-size`: Descending sort of IfAddrs by their network size as determined by their + netmask (smaller networks first) + - `type`, `+type`: Ascending sort of IfAddrs by the type of the IfAddr (Unix, + IPv4, then IPv6) + - `-type`: Descending sort of IfAddrs by the type of the IfAddr (IPv6, IPv4, Unix) + +Example: + + {{ GetPrivateInterfaces | sort "type,size,address" }} + + +`exclude` and `include`: Filters IfAddrs based on the selector criteria and its +arguments. Both `exclude` and `include` take two arguments. The list of +available filtering criteria is: + - "address": Filter IfAddrs based on a regexp matching the string representation + of the address + - "flag","flags": Filter IfAddrs based on the list of flags specified. Multiple + flags can be passed together using the pipe character (`|`) to create an inclusive + bitmask of flags. The list of flags is included below. + - "name": Filter IfAddrs based on a regexp matching the interface name. + - "network": Filter IfAddrs based on whether a netowkr is included in a given + CIDR. More than one CIDR can be passed in if each network is separated by + the pipe character (`|`). + - "port": Filter IfAddrs based on an exact match of the port number (number must + be expressed as a string) + - "rfc", "rfcs": Filter IfAddrs based on the matching RFC. If more than one RFC + is specified, the list of RFCs can be joined together using the pipe character (`|`). + - "size": Filter IfAddrs based on the exact match of the mask size. + - "type": Filter IfAddrs based on their SockAddr type. Multiple types can be + specified together by using the pipe character (`|`). Valid types include: + `ip`, `ipv4`, `ipv6`, and `unix`. + +Example: + + {{ GetPrivateInterfaces | exclude "type" "IPv6" | include "flag" "up|forwardable" }} + + +`unique`: Removes duplicate entries from the IfAddrs list, assuming the list has +already been sorted. `unique` only takes one argument: + - "address": Removes duplicates with the same address + - "name": Removes duplicates with the same interface names + +Example: + + {{ GetPrivateInterfaces | sort "type,address" | unique "name" }} + + +`limit`: Reduces the size of the list to the specified value. + +Example: + + {{ GetPrivateInterfaces | include "flags" "forwardable|up" | limit 1 }} + + +`offset`: Seeks into the list by the specified value. A negative value can be +used to seek from the end of the list. + +Example: + + {{ GetPrivateInterfaces | include "flags" "forwardable|up" | offset "-2" | limit 1 }} + + +`attr`: Extracts a single attribute of the first member of the list and returns +it as a string. `attr` takes a single attribute name. The list of available +attributes is type-specific and shared between `join`. See below for a list of +supported attributes. + +Example: + + {{ GetPrivateInterfaces | include "flags" "forwardable|up" | attr "address" }} + + +`join`: Similar to `attr`, `join` extracts all matching attributes of the list +and returns them as a string joined by the separator, the second argument to +`join`. The list of available attributes is type-specific and shared between +`join`. + +Example: + + {{ GetPrivateInterfaces | include "flags" "forwardable|up" | join "address" " " }} + + +`exclude` and `include` flags: + - `broadcast` + - `down`: Is the interface down? + - `forwardable`: Is the IP forwardable? + - `global unicast` + - `interface-local multicast` + - `link-local multicast` + - `link-local unicast` + - `loopback` + - `multicast` + - `point-to-point` + - `unspecified`: Is the IfAddr the IPv6 unspecified address? + - `up`: Is the interface up? + + +Attributes for `attr` and `join`: + +SockAddr Type: + - `string` + - `type` + +IPAddr Type: + - `address` + - `binary` + - `first_usable` + - `hex` + - `host` + - `last_usable` + - `mask_bits` + - `netmask` + - `network` + - `octets`: Decimal values per byte + - `port` + - `size`: Number of hosts in the network + +IPv4Addr Type: + - `broadcast` + - `uint32`: unsigned integer representation of the value + +IPv6Addr Type: + - `uint128`: unsigned integer representation of the value + +UnixSock Type: + - `path` + +*/ +package template diff --git a/vendor/github.com/hashicorp/go-sockaddr/template/template.go b/vendor/github.com/hashicorp/go-sockaddr/template/template.go new file mode 100644 index 000000000000..ffe467b7f55f --- /dev/null +++ b/vendor/github.com/hashicorp/go-sockaddr/template/template.go @@ -0,0 +1,125 @@ +package template + +import ( + "bytes" + "fmt" + "text/template" + + "github.com/hashicorp/errwrap" + sockaddr "github.com/hashicorp/go-sockaddr" +) + +var ( + // SourceFuncs is a map of all top-level functions that generate + // sockaddr data types. + SourceFuncs template.FuncMap + + // SortFuncs is a map of all functions used in sorting + SortFuncs template.FuncMap + + // FilterFuncs is a map of all functions used in sorting + FilterFuncs template.FuncMap + + // HelperFuncs is a map of all functions used in sorting + HelperFuncs template.FuncMap +) + +func init() { + SourceFuncs = template.FuncMap{ + // GetAllInterfaces - Returns an exhaustive set of IfAddr + // structs available on the host. `GetAllInterfaces` is the + // initial input and accessible as the initial "dot" in the + // pipeline. + "GetAllInterfaces": sockaddr.GetAllInterfaces, + + // GetDefaultInterfaces - Returns one IfAddr for every IP that + // is on the interface containing the default route for the + // host. + "GetDefaultInterfaces": sockaddr.GetDefaultInterfaces, + + // GetPrivateInterfaces - Returns one IfAddr for every IP that + // matches RFC 6890, are attached to the interface with the + // default route, and are forwardable IP addresses. NOTE: RFC + // 6890 is a more exhaustive version of RFC1918 because it spans + // IPv4 and IPv6, however it doespermit the inclusion of likely + // undesired addresses such as multicast, therefore our + // definition of a "private" address also excludes + // non-forwardable IP addresses (as defined by the IETF). + "GetPrivateInterfaces": sockaddr.GetPrivateInterfaces, + + // GetPublicInterfaces - Returns a list of IfAddr that do not + // match RFC 6890, are attached to the default route, and are + // forwardable. + "GetPublicInterfaces": sockaddr.GetPublicInterfaces, + } + + SortFuncs = template.FuncMap{ + "sort": sockaddr.SortIfBy, + } + + FilterFuncs = template.FuncMap{ + "exclude": sockaddr.ExcludeIfs, + "include": sockaddr.IncludeIfs, + } + + HelperFuncs = template.FuncMap{ + // Misc functions that operate on IfAddrs inputs + "attr": sockaddr.IfAttr, + "join": sockaddr.JoinIfAddrs, + "limit": sockaddr.LimitIfAddrs, + "offset": sockaddr.OffsetIfAddrs, + "unique": sockaddr.UniqueIfAddrsBy, + + // Return a Private RFC 6890 IP address string that is attached + // to the default route and a forwardable address. + "GetPrivateIP": sockaddr.GetPrivateIP, + + // Return a Public RFC 6890 IP address string that is attached + // to the default route and a forwardable address. + "GetPublicIP": sockaddr.GetPublicIP, + + // Return the first IP address of the named interface, sorted by + // the largest network size. + "GetInterfaceIP": sockaddr.GetInterfaceIP, + } +} + +// Parse parses input as template input using the addresses available on the +// host, then returns the string output if there are no errors. +func Parse(input string) (string, error) { + addrs, err := sockaddr.GetAllInterfaces() + if err != nil { + return "", errwrap.Wrapf("unable to query interface addresses: {{err}}", err) + } + + return ParseIfAddrs(input, addrs) +} + +// ParseIfAddrs parses input as template input using the IfAddrs inputs, then +// returns the string output if there are no errors. +func ParseIfAddrs(input string, ifAddrs sockaddr.IfAddrs) (string, error) { + return ParseIfAddrsTemplate(input, ifAddrs, template.New("sockaddr.Parse")) +} + +// ParseIfAddrsTemplate parses input as template input using the IfAddrs inputs, +// then returns the string output if there are no errors. +func ParseIfAddrsTemplate(input string, ifAddrs sockaddr.IfAddrs, tmplIn *template.Template) (string, error) { + // Create a template, add the function map, and parse the text. + tmpl, err := tmplIn.Option("missingkey=error"). + Funcs(SourceFuncs). + Funcs(SortFuncs). + Funcs(FilterFuncs). + Funcs(HelperFuncs). + Parse(input) + if err != nil { + return "", errwrap.Wrapf(fmt.Sprintf("unable to parse template %+q: {{err}}", input), err) + } + + var outWriter bytes.Buffer + err = tmpl.Execute(&outWriter, ifAddrs) + if err != nil { + return "", errwrap.Wrapf(fmt.Sprintf("unable to execute sockaddr input %+q: {{err}}", input), err) + } + + return outWriter.String(), nil +} diff --git a/vendor/vendor.json b/vendor/vendor.json index 90c73474440e..acc3458ad5c6 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -738,6 +738,12 @@ "revision": "f910dd83c2052566cad78352c33af714358d1372", "revisionTime": "2017-02-08T07:30:35Z" }, + { + "checksumSHA1": "lPzwetgfMBtpHqdTPolgejMctVQ=", + "path": "github.com/hashicorp/go-sockaddr/template", + "revision": "f910dd83c2052566cad78352c33af714358d1372", + "revisionTime": "2017-02-08T07:30:35Z" + }, { "path": "github.com/hashicorp/go-syslog", "revision": "42a2b573b664dbf281bd48c3cc12c086b17a39ba" diff --git a/website/source/docs/agent/configuration/index.html.md b/website/source/docs/agent/configuration/index.html.md index c699d641a62d..c9adc2c9f8c4 100644 --- a/website/source/docs/agent/configuration/index.html.md +++ b/website/source/docs/agent/configuration/index.html.md @@ -81,6 +81,7 @@ testing. - `addresses` `(Addresses: see below)` - Specifies the bind address for individual network services. Any values configured in this stanza take precedence over the default [bind_addr](#bind_addr). + The values support [go-sockaddr/template format][go-sockaddr/template]. - `http` - The address the HTTP server is bound to. This is the most common bind address to change. @@ -100,6 +101,7 @@ testing. values configured in this stanza take precedence over the default [bind_addr](#bind_addr). If the bind address is `0.0.0.0` then the hostname is advertised. You may advertise an alternate port as well. + The values support [go-sockaddr/template format][go-sockaddr/template]. - `http` - The address to advertise for the HTTP interface. This should be reachable by all the nodes from which end users are going to use the Nomad @@ -122,6 +124,7 @@ testing. the same address. It is also possible to bind the individual services to different addresses using the [addresses](#addresses) configuration option. Dev mode (`-dev`) defaults to localhost. + The value supports [go-sockaddr/template format][go-sockaddr/template]. - `client` ([Client][client]: nil) - Specifies configuration which is specific to the Nomad client. @@ -228,6 +231,7 @@ http_api_response_headers { ``` [hcl]: https://github.com/hashicorp/hcl "HashiCorp Configuration Language" +[go-sockaddr/template]: https://godoc.org/github.com/hashicorp/go-sockaddr/template [consul]: /docs/agent/configuration/consul.html "Nomad Agent consul Configuration" [atlas]: /docs/agent/configuration/atlas.html "Nomad Agent atlas Configuration" [vault]: /docs/agent/configuration/vault.html "Nomad Agent vault Configuration"