summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/hashicorp/go-sockaddr/template
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/hashicorp/go-sockaddr/template')
-rw-r--r--vendor/github.com/hashicorp/go-sockaddr/template/Makefile2
-rw-r--r--vendor/github.com/hashicorp/go-sockaddr/template/README.md6
-rw-r--r--vendor/github.com/hashicorp/go-sockaddr/template/doc.go239
-rw-r--r--vendor/github.com/hashicorp/go-sockaddr/template/template.go125
-rw-r--r--vendor/github.com/hashicorp/go-sockaddr/template/template_test.go203
5 files changed, 575 insertions, 0 deletions
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 000000000..ce1e274e4
--- /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 000000000..c40905af7
--- /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 000000000..59945d7be
--- /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 000000000..ffe467b7f
--- /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/github.com/hashicorp/go-sockaddr/template/template_test.go b/vendor/github.com/hashicorp/go-sockaddr/template/template_test.go
new file mode 100644
index 000000000..6f2b47828
--- /dev/null
+++ b/vendor/github.com/hashicorp/go-sockaddr/template/template_test.go
@@ -0,0 +1,203 @@
+package template_test
+
+import (
+ "testing"
+
+ socktmpl "github.com/hashicorp/go-sockaddr/template"
+)
+
+func TestSockAddr_Parse(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ output string
+ fail bool
+ requireOnline bool
+ }{
+ {
+ name: `basic include "name"`,
+ input: `{{GetAllInterfaces | include "name" "lo0" | printf "%v"}}`,
+ output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`,
+ },
+ {
+ name: "invalid input",
+ input: `{{`,
+ output: ``,
+ fail: true,
+ },
+ {
+ name: "GetDefaultInterface",
+ input: `{{GetDefaultInterfaces | include "type" "IPv4" | attr "name" }}`,
+ output: `en0`,
+ },
+ {
+ name: `include "name" regexp`,
+ input: `{{GetAllInterfaces | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`,
+ output: `127.0.0.1 ::1 fe80::1`,
+ },
+ {
+ name: `exclude "name"`,
+ input: `{{. | include "name" "^(en|lo)0$" | exclude "name" "^en0$" | sort "type" | sort "address" | join "address" " " }}`,
+ output: `127.0.0.1 ::1 fe80::1`,
+ },
+ {
+ name: `"dot" pipeline, IPv4 type`,
+ input: `{{. | include "type" "IPv4" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
+ output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast}]`,
+ },
+ {
+ name: `include "type" "IPv6`,
+ input: `{{. | include "type" "IPv6" | include "name" "^lo0$" | sort "address" }}`,
+ output: `[::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`,
+ },
+ {
+ name: "better example for IP types",
+ input: `{{. | include "type" "IPv4|IPv6" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
+ output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`,
+ },
+ {
+ name: "ifAddrs1",
+ input: `{{. | include "type" "IPv4" | include "name" "^lo0$"}}`,
+ output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast}]`,
+ },
+ {
+ name: "ifAddrs2",
+ input: `{{. | include "type" "IP" | include "name" "^lo0$" | sort "type" | sort "address" }}`,
+ output: `[127.0.0.1/8 {1 16384 lo0 up|loopback|multicast} ::1 {1 16384 lo0 up|loopback|multicast} fe80::1/64 {1 16384 lo0 up|loopback|multicast}]`,
+ },
+ {
+ name: `range "dot" example`,
+ input: `{{range . | include "type" "IP" | include "name" "^lo0$"}}{{.Name}} {{.SockAddr}} {{end}}`,
+ output: `lo0 127.0.0.1/8 lo0 ::1 lo0 fe80::1/64 `,
+ },
+ {
+ name: `exclude "type"`,
+ input: `{{. | exclude "type" "IPv4" | include "name" "^lo0$" | sort "address" | unique "name" | join "name" " "}} {{range . | exclude "type" "IPv4" | include "name" "^lo0$"}}{{.SockAddr}} {{end}}`,
+ output: `lo0 ::1 fe80::1/64 `,
+ },
+ {
+ name: "with variable pipeline",
+ input: `{{with $ifSet := include "type" "IPv4" . | include "name" "^lo0$"}}{{range $ifSet }}{{.Name}} {{end}}{{range $ifSet}}{{.SockAddr}} {{end}}{{end}}`,
+ output: `lo0 127.0.0.1/8 `,
+ },
+ {
+ name: "range sample on lo0",
+ input: `{{with $ifAddrs := . | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" }}{{range $ifAddrs }}{{.Name}}/{{.SockAddr.NetIP}} {{end}}{{end}}`,
+ output: `lo0/127.0.0.1 lo0/::1 lo0/fe80::1 `,
+ },
+ {
+ name: "non-RFC1918 on on lo0",
+ input: `{{. | exclude "rfc" "1918" | include "name" "lo0" | sort "type,address" | len | eq 3}}`,
+ output: `true`,
+ },
+ {
+ // NOTE(sean@): Difficult to reliably test includeByRFC.
+ // In this case, we ass-u-me that the host running the
+ // test has at least one RFC1918 address on their host.
+ name: `include "rfc"`,
+ input: `{{(. | include "rfc" "1918" | attr "name")}}`,
+ output: `en0`,
+ requireOnline: true,
+ },
+ {
+ name: "test for non-empty array",
+ input: `{{. | include "type" "IPv4" | include "rfc" "1918" | print | len | eq (len "[]")}}`,
+ output: `false`,
+ },
+ {
+ // NOTE(sean@): This will fail if there is a public IPv4 address on loopback.
+ name: "non-IPv4 RFC1918",
+ input: `{{. | include "name" "lo0" | exclude "type" "IPv4" | include "rfc" "1918" | len | eq 0}}`,
+ output: `true`,
+ },
+ {
+ // NOTE(sean@): There are no RFC6598 addresses on most testing hosts so this should be empty.
+ name: "rfc6598",
+ input: `{{. | include "type" "IPv4" | include "rfc" "6598" | print | len | eq (len "[]")}}`,
+ output: `true`,
+ },
+ {
+ name: "invalid RFC",
+ input: `{{. | include "type" "IPv4" | include "rfc" "99999999999" | print | len | eq (len "[]")}}`,
+ output: `true`,
+ fail: true,
+ },
+ {
+ name: `sort asc address`,
+ input: `{{ . | include "name" "lo0" | sort "type,address" | join "address" " " }}`,
+ output: `127.0.0.1 ::1 fe80::1`,
+ },
+ {
+ name: `sort asc address old`,
+ input: `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv4" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`,
+ output: `127.0.0.1/8 ::1 fe80::1/64 `,
+ },
+ {
+ name: `sort desc address`,
+ input: `{{ . | include "name" "lo0" | sort "type,-address" | join "address" " " }}`,
+ output: `127.0.0.1 fe80::1 ::1`,
+ },
+ {
+ name: `sort desc address`,
+ input: `{{ . | include "name" "lo0" | include "type" "IPv6" | sort "type,-address" | join "address" " " }}`,
+ output: `fe80::1 ::1`,
+ },
+ {
+ name: `sort asc address`,
+ input: `{{with $ifSet := include "name" "lo0" . }}{{ range include "type" "IPv6" $ifSet | sort "address"}}{{ .SockAddr }} {{end}}{{end}}`,
+ output: `::1 fe80::1/64 `,
+ },
+ {
+ name: "lo0 limit 1",
+ input: `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | limit 1 | len}}`,
+ output: `1`,
+ },
+ {
+ name: "join address",
+ input: `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "address" " " }}`,
+ output: `::1 fe80::1`,
+ },
+ {
+ name: "join name",
+ input: `{{. | include "name" "lo0" | include "type" "IPv6" | sort "address" | join "name" " " }}`,
+ output: `lo0 lo0`,
+ },
+ {
+ name: "lo0 flags up and limit 1",
+ input: `{{. | include "name" "lo0" | include "flag" "up" | sort "-type,+address" | attr "address" }}`,
+ output: `::1`,
+ },
+ {
+ // NOTE(sean@): This is the HashiCorp default in 2016.
+ // Indented for effect. Using "true" as the output
+ // instead of printing the correct $rfc*Addrs values.
+ name: "HashiCorpDefault2016",
+ input: `
+{{- with $addr := GetAllInterfaces | include "type" "IP" | include "rfc" "1918|6598" | sort "address" | attr "address" -}}
+
+ {{- if ($addr | len) gt 0 -}}
+ {{- print "true" -}}{{/* print $addr*/ -}}
+ {{- end -}}
+{{- end -}}`,
+ output: `true`,
+ },
+ }
+
+ for i, test := range tests {
+ test := test // capture range variable
+ if test.name == "" {
+ t.Fatalf("test number %d has an empty test name", i)
+ }
+ t.Run(test.name, func(t *testing.T) {
+ t.Parallel()
+ out, err := socktmpl.Parse(test.input)
+ if err != nil && !test.fail {
+ t.Fatalf("%q: bad: %v", test.name, err)
+ }
+
+ if out != test.output && !test.fail {
+ t.Fatalf("%q: Expected %+q, received %+q\n%+q", test.name, test.output, out, test.input)
+ }
+ })
+ }
+}