From 742fcf7a24659801c0b21a4ccfe821a077d97fc2 Mon Sep 17 00:00:00 2001 From: James Phillips Date: Tue, 16 Aug 2016 10:30:30 -0700 Subject: [PATCH 1/3] Adds missing TaggedAddress structures to API client. --- api/catalog.go | 17 ++++++++++------- api/catalog_test.go | 9 +++++++++ api/health_test.go | 3 +++ api/prepared_query_test.go | 9 +++++++++ 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/api/catalog.go b/api/catalog.go index 52a00b304..337772ec0 100644 --- a/api/catalog.go +++ b/api/catalog.go @@ -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 @@ -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 { diff --git a/api/catalog_test.go b/api/catalog_test.go index 3a3395ae6..9ca832304 100644 --- a/api/catalog_test.go +++ b/api/catalog_test.go @@ -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) @@ -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) diff --git a/api/health_test.go b/api/health_test.go index d80a4693a..b6f463d56 100644 --- a/api/health_test.go +++ b/api/health_test.go @@ -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) diff --git a/api/prepared_query_test.go b/api/prepared_query_test.go index 011bfb195..d16f007c9 100644 --- a/api/prepared_query_test.go +++ b/api/prepared_query_test.go @@ -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", @@ -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) @@ -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) From 817d7e93e42a66e7db9645e7bf2a1413bdf55c6f Mon Sep 17 00:00:00 2001 From: James Phillips Date: Tue, 16 Aug 2016 10:49:03 -0700 Subject: [PATCH 2/3] Adds an "lan" tagged address so we have a way to get them all. If we didn't have this, then there would be no way to know the LAN address if address translation was turned on. --- command/agent/agent.go | 1 + command/agent/agent_test.go | 1 + website/source/docs/agent/http/catalog.html.markdown | 9 +++++++-- website/source/docs/agent/http/health.html.markdown | 1 + website/source/docs/agent/http/query.html.markdown | 6 +++++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/command/agent/agent.go b/command/agent/agent.go index 79b94d55d..d22cc92f1 100644 --- a/command/agent/agent.go +++ b/command/agent/agent.go @@ -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, } diff --git a/command/agent/agent_test.go b/command/agent/agent_test.go index bab39a7d0..6b94c119d 100644 --- a/command/agent/agent_test.go +++ b/command/agent/agent_test.go @@ -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) { diff --git a/website/source/docs/agent/http/catalog.html.markdown b/website/source/docs/agent/http/catalog.html.markdown index 55879a8ba..327daf935 100644 --- a/website/source/docs/agent/http/catalog.html.markdown +++ b/website/source/docs/agent/http/catalog.html.markdown @@ -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", @@ -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. @@ -200,6 +202,7 @@ 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" } }, @@ -207,6 +210,7 @@ It returns a JSON body like this: "Node": "foobar", "Address": "10.1.10.12", "TaggedAddresses": { + "lan": "10.1.10.11", "wan": "10.1.10.12" } } @@ -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" } }, diff --git a/website/source/docs/agent/http/health.html.markdown b/website/source/docs/agent/http/health.html.markdown index 6f0c4a99e..0825a1f47 100644 --- a/website/source/docs/agent/http/health.html.markdown +++ b/website/source/docs/agent/http/health.html.markdown @@ -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" } }, diff --git a/website/source/docs/agent/http/query.html.markdown b/website/source/docs/agent/http/query.html.markdown index d2737384d..5eaccdb11 100644 --- a/website/source/docs/agent/http/query.html.markdown +++ b/website/source/docs/agent/http/query.html.markdown @@ -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", From bc333335be355833d77652f794b4aac504bcaefa Mon Sep 17 00:00:00 2001 From: James Phillips Date: Tue, 16 Aug 2016 11:31:41 -0700 Subject: [PATCH 3/3] Adds an `X-Consul-Translate-Addresses` to signal translation is enabled. --- api/api.go | 12 +++++ api/api_test.go | 4 ++ command/agent/http.go | 10 +++++ command/agent/http_test.go | 45 +++++++++++++++++++ website/source/docs/agent/http.html.markdown | 9 ++++ .../source/docs/agent/options.html.markdown | 11 +++-- 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/api/api.go b/api/api.go index b0fe6d83b..dd811fde4 100644 --- a/api/api.go +++ b/api/api.go @@ -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 @@ -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 } diff --git a/api/api_test.go b/api/api_test.go index 71f01c483..b00101bd3 100644 --- a/api/api_test.go +++ b/api/api_test.go @@ -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 { @@ -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) { diff --git a/command/agent/http.go b/command/agent/http.go index cd3620454..5d7dcce7c 100644 --- a/command/agent/http.go +++ b/command/agent/http.go @@ -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) @@ -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 @@ -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)) diff --git a/command/agent/http_test.go b/command/agent/http_test.go index b6618977f..7cc80cb24 100644 --- a/command/agent/http_test.go +++ b/command/agent/http_test.go @@ -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{ diff --git a/website/source/docs/agent/http.html.markdown b/website/source/docs/agent/http.html.markdown index 6ea2b03f0..0f226ed84 100644 --- a/website/source/docs/agent/http.html.markdown +++ b/website/source/docs/agent/http.html.markdown @@ -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. + + +## 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. diff --git a/website/source/docs/agent/options.html.markdown b/website/source/docs/agent/options.html.markdown index 506a361ff..4409cbf71 100644 --- a/website/source/docs/agent/options.html.markdown +++ b/website/source/docs/agent/options.html.markdown @@ -756,9 +756,14 @@ Consul will not enable TLS for the HTTP API unless the `https` port has been ass default.

- Starting in Consul 0.7 and later, node addresses in responses to the following HTTP endpoints will - prefer a node's configured WAN address 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 WAN address 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. +
+
The following endpoints translate addresses:
* [`/v1/catalog/nodes`](/docs/agent/http/catalog.html#catalog_nodes) * [`/v1/catalog/node/`](/docs/agent/http/catalog.html#catalog_node)