Add support for late binding to IP addresses using go-sockaddr/template
This commit is contained in:
parent
e14c4e3ee4
commit
72b0a7f34d
|
@ -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"
|
||||
|
@ -692,22 +695,45 @@ 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)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse HTTP advertise address: %v", err)
|
||||
}
|
||||
c.AdvertiseAddrs.HTTP = addr
|
||||
|
||||
addr, err = normalizeAdvertise(c.AdvertiseAddrs.RPC, c.Addresses.RPC, c.Ports.RPC, c.DevMode)
|
||||
addr, err = normalizeAdvertise(c.AdvertiseAddrs.RPC, c.Addresses.RPC, c.Ports.RPC)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse RPC advertise address: %v", err)
|
||||
}
|
||||
|
@ -715,7 +741,7 @@ func (c *Config) normalizeAddrs() error {
|
|||
|
||||
// Skip serf if server is disabled
|
||||
if c.Server != nil && c.Server.Enabled {
|
||||
addr, err = normalizeAdvertise(c.AdvertiseAddrs.Serf, c.Addresses.Serf, c.Ports.Serf, c.DevMode)
|
||||
addr, err = normalizeAdvertise(c.AdvertiseAddrs.Serf, c.Addresses.Serf, c.Ports.Serf)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to parse Serf advertise address: %v", err)
|
||||
}
|
||||
|
@ -725,14 +751,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.
|
||||
|
@ -747,61 +793,28 @@ func normalizeBind(addr, bind string) string {
|
|||
// is resolved and returned with the port.
|
||||
//
|
||||
// Loopback is only considered a valid advertise address in dev mode.
|
||||
func normalizeAdvertise(addr string, bind string, defport int, dev bool) (string, error) {
|
||||
func normalizeAdvertise(addr string, bind string, defport int) (string, error) {
|
||||
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
|
||||
}
|
||||
|
||||
// Fallback to bind address first, and then try resolving the local hostname
|
||||
ips, err := net.LookupIP(bind)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error resolving bind address %q: %v", bind, 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
|
||||
ipStr, err := parseSingleIPTemplate(host)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("Error parsing advertise address template: %v", err)
|
||||
}
|
||||
|
||||
return net.JoinHostPort(ipStr, port), 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()
|
||||
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("No valid advertise addresses, please set `advertise` manually")
|
||||
// Fallback to bind address, as it has been resolved before.
|
||||
return net.JoinHostPort(bind, strconv.Itoa(defport)), nil
|
||||
}
|
||||
|
||||
// isMissingPort returns true if an error is a "missing port" error from
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package agent
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
|
@ -520,6 +521,121 @@ func TestConfig_Listener(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestConfig_normalizeAddrs(t *testing.T) {
|
||||
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)
|
||||
}
|
||||
|
||||
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{},
|
||||
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:4647", c.BindAddr) {
|
||||
t.Fatalf("expected RPC advertise address %s:4647, 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)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResources_ParseReserved(t *testing.T) {
|
||||
cases := []struct {
|
||||
Input string
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
test::
|
||||
go test
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
||||
}
|
|
@ -793,6 +793,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"
|
||||
|
|
Loading…
Reference in New Issue