Skip to content

Commit

Permalink
Merge pull request #2280 from hashicorp/f-lan-tag
Browse files Browse the repository at this point in the history
Adds missing TaggedAddress structures to Go API client, adapts for address translation.
  • Loading branch information
slackpad authored Aug 16, 2016
2 parents 315b9d4 + 9f7a973 commit 1d130a6
Show file tree
Hide file tree
Showing 15 changed files with 134 additions and 13 deletions.
12 changes: 12 additions & 0 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,9 @@ type QueryMeta struct {

// How long did the request take
RequestTime time.Duration

// Is address translation enabled for HTTP responses on this agent
AddressTranslationEnabled bool
}

// WriteMeta is used to return meta data about a write
Expand Down Expand Up @@ -542,6 +545,15 @@ func parseQueryMeta(resp *http.Response, q *QueryMeta) error {
default:
q.KnownLeader = false
}

// Parse X-Consul-Translate-Addresses
switch header.Get("X-Consul-Translate-Addresses") {
case "true":
q.AddressTranslationEnabled = true
default:
q.AddressTranslationEnabled = false
}

return nil
}

Expand Down
4 changes: 4 additions & 0 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ func TestParseQueryMeta(t *testing.T) {
resp.Header.Set("X-Consul-Index", "12345")
resp.Header.Set("X-Consul-LastContact", "80")
resp.Header.Set("X-Consul-KnownLeader", "true")
resp.Header.Set("X-Consul-Translate-Addresses", "true")

qm := &QueryMeta{}
if err := parseQueryMeta(resp, qm); err != nil {
Expand All @@ -321,6 +322,9 @@ func TestParseQueryMeta(t *testing.T) {
if !qm.KnownLeader {
t.Fatalf("Bad: %v", qm)
}
if !qm.AddressTranslationEnabled {
t.Fatalf("Bad: %v", qm)
}
}

func TestAPI_UnixSocket(t *testing.T) {
Expand Down
17 changes: 10 additions & 7 deletions api/catalog.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package api

type Node struct {
Node string
Address string
Node string
Address string
TaggedAddresses map[string]string
}

type CatalogService struct {
Node string
Address string
TaggedAddresses map[string]string
ServiceID string
ServiceName string
ServiceAddress string
Expand All @@ -22,11 +24,12 @@ type CatalogNode struct {
}

type CatalogRegistration struct {
Node string
Address string
Datacenter string
Service *AgentService
Check *AgentCheck
Node string
Address string
TaggedAddresses map[string]string
Datacenter string
Service *AgentService
Check *AgentCheck
}

type CatalogDeregistration struct {
Expand Down
9 changes: 9 additions & 0 deletions api/catalog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ func TestCatalog_Nodes(t *testing.T) {
return false, fmt.Errorf("Bad: %v", nodes)
}

if _, ok := nodes[0].TaggedAddresses["wan"]; !ok {
return false, fmt.Errorf("Bad: %v", nodes)
}

return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
Expand Down Expand Up @@ -128,10 +132,15 @@ func TestCatalog_Node(t *testing.T) {
if meta.LastIndex == 0 {
return false, fmt.Errorf("Bad: %v", meta)
}

if len(info.Services) == 0 {
return false, fmt.Errorf("Bad: %v", info)
}

if _, ok := info.Node.TaggedAddresses["wan"]; !ok {
return false, fmt.Errorf("Bad: %v", info)
}

return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
Expand Down
3 changes: 3 additions & 0 deletions api/health_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ func TestHealth_Service(t *testing.T) {
if len(checks) == 0 {
return false, fmt.Errorf("Bad: %v", checks)
}
if _, ok := checks[0].Node.TaggedAddresses["wan"]; !ok {
return false, fmt.Errorf("Bad: %v", checks)
}
return true, nil
}, func(err error) {
t.Fatalf("err: %s", err)
Expand Down
9 changes: 9 additions & 0 deletions api/prepared_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ func TestPreparedQuery(t *testing.T) {
Datacenter: "dc1",
Node: "foobar",
Address: "192.168.10.10",
TaggedAddresses: map[string]string{
"wan": "127.0.0.1",
},
Service: &AgentService{
ID: "redis1",
Service: "redis",
Expand Down Expand Up @@ -96,6 +99,9 @@ func TestPreparedQuery(t *testing.T) {
if len(results.Nodes) != 1 || results.Nodes[0].Node.Node != "foobar" {
t.Fatalf("bad: %v", results)
}
if wan, ok := results.Nodes[0].Node.TaggedAddresses["wan"]; !ok || wan != "127.0.0.1" {
t.Fatalf("bad: %v", results)
}

// Execute by name.
results, _, err = query.Execute("my-query", nil)
Expand All @@ -105,6 +111,9 @@ func TestPreparedQuery(t *testing.T) {
if len(results.Nodes) != 1 || results.Nodes[0].Node.Node != "foobar" {
t.Fatalf("bad: %v", results)
}
if wan, ok := results.Nodes[0].Node.TaggedAddresses["wan"]; !ok || wan != "127.0.0.1" {
t.Fatalf("bad: %v", results)
}

// Delete it.
_, err = query.Delete(def.ID, nil)
Expand Down
1 change: 1 addition & 0 deletions command/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ func Create(config *Config, logOutput io.Writer) (*Agent, error) {

// Create the default set of tagged addresses.
config.TaggedAddresses = map[string]string{
"lan": config.AdvertiseAddr,
"wan": config.AdvertiseAddrWan,
}

Expand Down
1 change: 1 addition & 0 deletions command/agent/agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ func TestAgent_CheckAdvertiseAddrsSettings(t *testing.T) {
t.Fatalf("RPC is not properly set to %v: %s", c.AdvertiseAddrs.RPC, rpc)
}
expected := map[string]string{
"lan": agent.config.AdvertiseAddr,
"wan": agent.config.AdvertiseAddrWan,
}
if !reflect.DeepEqual(agent.config.TaggedAddresses, expected) {
Expand Down
10 changes: 10 additions & 0 deletions command/agent/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ func (s *HTTPServer) registerHandlers(enableDebug bool) {
func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Request) (interface{}, error)) func(resp http.ResponseWriter, req *http.Request) {
f := func(resp http.ResponseWriter, req *http.Request) {
setHeaders(resp, s.agent.config.HTTPAPIResponseHeaders)
setTranslateAddr(resp, s.agent.config.TranslateWanAddrs)

// Obfuscate any tokens from appearing in the logs
formVals, err := url.ParseQuery(req.URL.RawQuery)
Expand Down Expand Up @@ -373,6 +374,7 @@ func (s *HTTPServer) wrap(handler func(resp http.ResponseWriter, req *http.Reque
if strings.Contains(errMsg, "Permission denied") || strings.Contains(errMsg, "ACL not found") {
code = http.StatusForbidden // 403
}

resp.WriteHeader(code)
resp.Write([]byte(err.Error()))
return
Expand Down Expand Up @@ -452,6 +454,14 @@ func decodeBody(req *http.Request, out interface{}, cb func(interface{}) error)
return mapstructure.Decode(raw, out)
}

// setTranslateAddr is used to set the address translation header. This is only
// present if the feature is active.
func setTranslateAddr(resp http.ResponseWriter, active bool) {
if active {
resp.Header().Set("X-Consul-Translate-Addresses", "true")
}
}

// setIndex is used to set the index response header
func setIndex(resp http.ResponseWriter, index uint64) {
resp.Header().Set("X-Consul-Index", strconv.FormatUint(index, 10))
Expand Down
45 changes: 45 additions & 0 deletions command/agent/http_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,51 @@ func TestSetMeta(t *testing.T) {
}
}

func TestHTTPAPI_TranslateAddrHeader(t *testing.T) {
// Header should not be present if address translation is off.
{
dir, srv := makeHTTPServer(t)
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()

resp := httptest.NewRecorder()
handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
return nil, nil
}

req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
srv.wrap(handler)(resp, req)

translate := resp.Header().Get("X-Consul-Translate-Addresses")
if translate != "" {
t.Fatalf("bad: expected %q, got %q", "", translate)
}
}

// Header should be set to true if it's turned on.
{
dir, srv := makeHTTPServer(t)
srv.agent.config.TranslateWanAddrs = true
defer os.RemoveAll(dir)
defer srv.Shutdown()
defer srv.agent.Shutdown()

resp := httptest.NewRecorder()
handler := func(resp http.ResponseWriter, req *http.Request) (interface{}, error) {
return nil, nil
}

req, _ := http.NewRequest("GET", "/v1/agent/self", nil)
srv.wrap(handler)(resp, req)

translate := resp.Header().Get("X-Consul-Translate-Addresses")
if translate != "true" {
t.Fatalf("bad: expected %q, got %q", "true", translate)
}
}
}

func TestHTTPAPIResponseHeaders(t *testing.T) {
dir, srv := makeHTTPServer(t)
srv.agent.config.HTTPAPIResponseHeaders = map[string]string{
Expand Down
9 changes: 9 additions & 0 deletions website/source/docs/agent/http.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,12 @@ configuration option. However, the token can also be specified per-request
by using the `X-Consul-Token` request header or the `token` querystring
parameter. The request header takes precedence over the default token, and
the querystring parameter takes precedence over everything.


## <a id="translate_header"></a>Translated Addresses

Consul 0.7 added the ability to translate addresses in HTTP response based on the configuration
setting for [`translate_wan_addrs`](/docs/agent/options.html#translate_wan_addrs). In order to
allow clients to know if address translation is in effect, the `X-Consul-Translate-Addresses`
header will be added if translation is enabled, and will have a value of `true`. If translation
is not enabled then this header will not be present.
9 changes: 7 additions & 2 deletions website/source/docs/agent/http/catalog.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ body must look something like:
"Node": "foobar",
"Address": "192.168.10.10",
"TaggedAddresses": {
"wan": "127.0.0.1"
"lan": "192.168.10.10",
"wan": "10.0.10.10"
},
"Service": {
"ID": "redis1",
Expand Down Expand Up @@ -69,7 +70,8 @@ requires `Node` and `Address` to be provided while `Datacenter` will be defaulte
to match that of the agent. If only those are provided, the endpoint will register
the node with the catalog. `TaggedAddresses` can be used in conjunction with the
[`translate_wan_addrs`](/docs/agent/options.html#translate_wan_addrs) configuration
option. Currently only the "wan" tag is supported.
option and the "wan" address. The "lan" address was added in Consul 0.7 to help find
the LAN address if address translation is enabled.

If the `Service` key is provided, the service will also be registered. If
`ID` is not provided, it will be defaulted to the value of the `Service.Service` property.
Expand Down Expand Up @@ -200,13 +202,15 @@ It returns a JSON body like this:
"Node": "baz",
"Address": "10.1.10.11",
"TaggedAddresses": {
"lan": "10.1.10.11",
"wan": "10.1.10.11"
}
},
{
"Node": "foobar",
"Address": "10.1.10.12",
"TaggedAddresses": {
"lan": "10.1.10.11",
"wan": "10.1.10.12"
}
}
Expand Down Expand Up @@ -287,6 +291,7 @@ It returns a JSON body like this:
"Node": "foobar",
"Address": "10.1.10.12",
"TaggedAddresses": {
"lan": "10.1.10.12",
"wan": "10.1.10.12"
}
},
Expand Down
1 change: 1 addition & 0 deletions website/source/docs/agent/http/health.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ It returns a JSON body like this:
"Node": "foobar",
"Address": "10.1.10.12",
"TaggedAddresses": {
"lan": "10.1.10.12",
"wan": "10.1.10.12"
}
},
Expand Down
6 changes: 5 additions & 1 deletion website/source/docs/agent/http/query.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,11 @@ a JSON body will be returned like this:
{
"Node": {
"Node": "foobar",
"Address": "10.1.10.12"
"Address": "10.1.10.12",
"TaggedAddresses": {
"lan": "10.1.10.12",
"wan": "10.1.10.12"
}
},
"Service": {
"ID": "redis",
Expand Down
11 changes: 8 additions & 3 deletions website/source/docs/agent/options.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -756,9 +756,14 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass
default.
<br>
<br>
Starting in Consul 0.7 and later, node addresses in responses to the following HTTP endpoints will
prefer a node's configured <a href="#_advertise-wan">WAN address</a> when querying for a node in a
remote datacenter:
Starting in Consul 0.7 and later, node addresses in responses to HTTP requests will also prefer a
node's configured <a href="#_advertise-wan">WAN address</a> when querying for a node in a remote
datacenter. An [`X-Consul-Translate-Addresses`](/docs/agent/http.html#translate_header) header
will be present on all responses when translation is enabled to help clients know that the addresses
may be translated. The `TaggedAddresses` field in responses also have a `lan` address for clients that
need knowledge of that address, regardless of translation.
<br>
<br>The following endpoints translate addresses:
<br>
* [`/v1/catalog/nodes`](/docs/agent/http/catalog.html#catalog_nodes)
* [`/v1/catalog/node/<node>`](/docs/agent/http/catalog.html#catalog_node)
Expand Down

0 comments on commit 1d130a6

Please sign in to comment.