mirror of https://github.com/status-im/consul.git
Add tagged addresses for services (#5965)
This allows addresses to be tagged at the service level similar to what we allow for nodes already. The address translation that can be enabled with the `translate_wan_addrs` config was updated to take these new addresses into account as well.
This commit is contained in:
parent
acfcc7daf4
commit
f3d9b999ee
|
@ -166,6 +166,15 @@ func buildAgentService(s *structs.NodeService, proxies map[string]*local.Managed
|
|||
}
|
||||
weights.Warning = s.Weights.Warning
|
||||
}
|
||||
|
||||
var taggedAddrs map[string]api.ServiceAddress
|
||||
if len(s.TaggedAddresses) > 0 {
|
||||
taggedAddrs = make(map[string]api.ServiceAddress)
|
||||
for k, v := range s.TaggedAddresses {
|
||||
taggedAddrs[k] = v.ToAPIServiceAddress()
|
||||
}
|
||||
}
|
||||
|
||||
as := api.AgentService{
|
||||
Kind: api.ServiceKind(s.Kind),
|
||||
ID: s.ID,
|
||||
|
@ -174,6 +183,7 @@ func buildAgentService(s *structs.NodeService, proxies map[string]*local.Managed
|
|||
Meta: s.Meta,
|
||||
Port: s.Port,
|
||||
Address: s.Address,
|
||||
TaggedAddresses: taggedAddrs,
|
||||
EnableTagOverride: s.EnableTagOverride,
|
||||
CreateIndex: s.CreateIndex,
|
||||
ModifyIndex: s.ModifyIndex,
|
||||
|
@ -887,6 +897,9 @@ func (s *HTTPServer) AgentRegisterService(resp http.ResponseWriter, req *http.Re
|
|||
// DON'T Recurse into these opaque config maps or we might mangle user's
|
||||
// keys. Note empty canonical is a special sentinel to prevent recursion.
|
||||
"Meta": "",
|
||||
|
||||
"tagged_addresses": "TaggedAddresses",
|
||||
|
||||
// upstreams is an array but this prevents recursion into config field of
|
||||
// any item in the array.
|
||||
"Proxy.Config": "",
|
||||
|
|
|
@ -2503,6 +2503,16 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
|
|||
"name":"test",
|
||||
"port":8000,
|
||||
"enable_tag_override": true,
|
||||
"tagged_addresses": {
|
||||
"lan": {
|
||||
"address": "1.2.3.4",
|
||||
"port": 5353
|
||||
},
|
||||
"wan": {
|
||||
"address": "2.3.4.5",
|
||||
"port": 53
|
||||
}
|
||||
},
|
||||
"meta": {
|
||||
"some": "meta",
|
||||
"enable_tag_override": "meta is 'opaque' so should not get translated"
|
||||
|
@ -2595,6 +2605,16 @@ func TestAgent_RegisterService_TranslateKeys(t *testing.T) {
|
|||
svc := &structs.NodeService{
|
||||
ID: "test",
|
||||
Service: "test",
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"lan": {
|
||||
Address: "1.2.3.4",
|
||||
Port: 5353,
|
||||
},
|
||||
"wan": {
|
||||
Address: "2.3.4.5",
|
||||
Port: 53,
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
"some": "meta",
|
||||
"enable_tag_override": "meta is 'opaque' so should not get translated",
|
||||
|
|
|
@ -284,8 +284,8 @@ RETRY_ONCE:
|
|||
goto RETRY_ONCE
|
||||
}
|
||||
out.ConsistencyLevel = args.QueryOptions.ConsistencyLevel()
|
||||
if out.NodeServices != nil && out.NodeServices.Node != nil {
|
||||
s.agent.TranslateAddresses(args.Datacenter, out.NodeServices.Node)
|
||||
if out.NodeServices != nil {
|
||||
s.agent.TranslateAddresses(args.Datacenter, out.NodeServices)
|
||||
}
|
||||
|
||||
// TODO: The NodeServices object in IndexedNodeServices is a pointer to
|
||||
|
|
|
@ -769,13 +769,10 @@ func TestCatalogServiceNodes_WanTranslation(t *testing.T) {
|
|||
|
||||
// Wait for the WAN join.
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN)
|
||||
if _, err := a2.srv.agent.JoinWAN([]string{addr}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
_, err := a2.srv.agent.JoinWAN([]string{addr})
|
||||
require.NoError(t, err)
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
if got, want := len(a1.WANMembers()), 2; got < want {
|
||||
r.Fatalf("got %d WAN members want at least %d", got, want)
|
||||
}
|
||||
require.Len(r, a1.WANMembers(), 2)
|
||||
})
|
||||
|
||||
// Register a node with DC2.
|
||||
|
@ -789,51 +786,51 @@ func TestCatalogServiceNodes_WanTranslation(t *testing.T) {
|
|||
},
|
||||
Service: &structs.NodeService{
|
||||
Service: "http_wan_translation_test",
|
||||
Address: "127.0.0.1",
|
||||
Port: 8080,
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "1.2.3.4",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var out struct{}
|
||||
if err := a2.RPC("Catalog.Register", args, &out); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
require.NoError(t, a2.RPC("Catalog.Register", args, &out))
|
||||
}
|
||||
|
||||
// Query for the node in DC2 from DC1.
|
||||
req, _ := http.NewRequest("GET", "/v1/catalog/service/http_wan_translation_test?dc=dc2", nil)
|
||||
resp1 := httptest.NewRecorder()
|
||||
obj1, err1 := a1.srv.CatalogServiceNodes(resp1, req)
|
||||
if err1 != nil {
|
||||
t.Fatalf("err: %v", err1)
|
||||
}
|
||||
assertIndex(t, resp1)
|
||||
require.NoError(t, err1)
|
||||
require.NoError(t, checkIndex(resp1))
|
||||
|
||||
// Expect that DC1 gives us a WAN address (since the node is in DC2).
|
||||
nodes1 := obj1.(structs.ServiceNodes)
|
||||
if len(nodes1) != 1 {
|
||||
t.Fatalf("bad: %v", obj1)
|
||||
}
|
||||
nodes1, ok := obj1.(structs.ServiceNodes)
|
||||
require.True(t, ok, "obj1 is not a structs.ServiceNodes")
|
||||
require.Len(t, nodes1, 1)
|
||||
node1 := nodes1[0]
|
||||
if node1.Address != "127.0.0.2" {
|
||||
t.Fatalf("bad: %v", node1)
|
||||
}
|
||||
require.Equal(t, node1.Address, "127.0.0.2")
|
||||
require.Equal(t, node1.ServiceAddress, "1.2.3.4")
|
||||
require.Equal(t, node1.ServicePort, 80)
|
||||
|
||||
// Query DC2 from DC2.
|
||||
resp2 := httptest.NewRecorder()
|
||||
obj2, err2 := a2.srv.CatalogServiceNodes(resp2, req)
|
||||
if err2 != nil {
|
||||
t.Fatalf("err: %v", err2)
|
||||
}
|
||||
assertIndex(t, resp2)
|
||||
require.NoError(t, err2)
|
||||
require.NoError(t, checkIndex(resp2))
|
||||
|
||||
// Expect that DC2 gives us a local address (since the node is in DC2).
|
||||
nodes2 := obj2.(structs.ServiceNodes)
|
||||
if len(nodes2) != 1 {
|
||||
t.Fatalf("bad: %v", obj2)
|
||||
}
|
||||
nodes2, ok := obj2.(structs.ServiceNodes)
|
||||
require.True(t, ok, "obj2 is not a structs.ServiceNodes")
|
||||
require.Len(t, nodes2, 1)
|
||||
node2 := nodes2[0]
|
||||
if node2.Address != "127.0.0.1" {
|
||||
t.Fatalf("bad: %v", node2)
|
||||
}
|
||||
require.Equal(t, node2.Address, "127.0.0.1")
|
||||
require.Equal(t, node2.ServiceAddress, "127.0.0.1")
|
||||
require.Equal(t, node2.ServicePort, 8080)
|
||||
}
|
||||
|
||||
func TestCatalogServiceNodes_DistanceSort(t *testing.T) {
|
||||
|
@ -1150,13 +1147,10 @@ func TestCatalogNodeServices_WanTranslation(t *testing.T) {
|
|||
|
||||
// Wait for the WAN join.
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN)
|
||||
if _, err := a2.srv.agent.JoinWAN([]string{addr}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
_, err := a2.srv.agent.JoinWAN([]string{addr})
|
||||
require.NoError(t, err)
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
if got, want := len(a1.WANMembers()), 2; got < want {
|
||||
r.Fatalf("got %d WAN members want at least %d", got, want)
|
||||
}
|
||||
require.Len(r, a1.WANMembers(), 2)
|
||||
})
|
||||
|
||||
// Register a node with DC2.
|
||||
|
@ -1170,49 +1164,53 @@ func TestCatalogNodeServices_WanTranslation(t *testing.T) {
|
|||
},
|
||||
Service: &structs.NodeService{
|
||||
Service: "http_wan_translation_test",
|
||||
Address: "127.0.0.1",
|
||||
Port: 8080,
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "1.2.3.4",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var out struct{}
|
||||
if err := a2.RPC("Catalog.Register", args, &out); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
require.NoError(t, a2.RPC("Catalog.Register", args, &out))
|
||||
}
|
||||
|
||||
// Query for the node in DC2 from DC1.
|
||||
req, _ := http.NewRequest("GET", "/v1/catalog/node/foo?dc=dc2", nil)
|
||||
resp1 := httptest.NewRecorder()
|
||||
obj1, err1 := a1.srv.CatalogNodeServices(resp1, req)
|
||||
if err1 != nil {
|
||||
t.Fatalf("err: %v", err1)
|
||||
}
|
||||
assertIndex(t, resp1)
|
||||
require.NoError(t, err1)
|
||||
require.NoError(t, checkIndex(resp1))
|
||||
|
||||
// Expect that DC1 gives us a WAN address (since the node is in DC2).
|
||||
services1 := obj1.(*structs.NodeServices)
|
||||
if len(services1.Services) != 1 {
|
||||
t.Fatalf("bad: %v", obj1)
|
||||
}
|
||||
service1 := services1.Node
|
||||
if service1.Address != "127.0.0.2" {
|
||||
t.Fatalf("bad: %v", service1)
|
||||
}
|
||||
service1, ok := obj1.(*structs.NodeServices)
|
||||
require.True(t, ok, "obj1 is not a *structs.NodeServices")
|
||||
require.NotNil(t, service1.Node)
|
||||
require.Equal(t, service1.Node.Address, "127.0.0.2")
|
||||
require.Len(t, service1.Services, 1)
|
||||
ns1, ok := service1.Services["http_wan_translation_test"]
|
||||
require.True(t, ok, "Missing service http_wan_translation_test")
|
||||
require.Equal(t, "1.2.3.4", ns1.Address)
|
||||
require.Equal(t, 80, ns1.Port)
|
||||
|
||||
// Query DC2 from DC2.
|
||||
resp2 := httptest.NewRecorder()
|
||||
obj2, err2 := a2.srv.CatalogNodeServices(resp2, req)
|
||||
if err2 != nil {
|
||||
t.Fatalf("err: %v", err2)
|
||||
}
|
||||
assertIndex(t, resp2)
|
||||
require.NoError(t, err2)
|
||||
require.NoError(t, checkIndex(resp2))
|
||||
|
||||
// Expect that DC2 gives us a private address (since the node is in DC2).
|
||||
services2 := obj2.(*structs.NodeServices)
|
||||
if len(services2.Services) != 1 {
|
||||
t.Fatalf("bad: %v", obj2)
|
||||
}
|
||||
service2 := services2.Node
|
||||
if service2.Address != "127.0.0.1" {
|
||||
t.Fatalf("bad: %v", service2)
|
||||
}
|
||||
service2 := obj2.(*structs.NodeServices)
|
||||
require.True(t, ok, "obj2 is not a *structs.NodeServices")
|
||||
require.NotNil(t, service2.Node)
|
||||
require.Equal(t, service2.Node.Address, "127.0.0.1")
|
||||
require.Len(t, service2.Services, 1)
|
||||
ns2, ok := service2.Services["http_wan_translation_test"]
|
||||
require.True(t, ok, "Missing service http_wan_translation_test")
|
||||
require.Equal(t, ns2.Address, "127.0.0.1")
|
||||
require.Equal(t, ns2.Port, 8080)
|
||||
}
|
||||
|
|
|
@ -1177,6 +1177,26 @@ func (b *Builder) checkVal(v *CheckDefinition) *structs.CheckDefinition {
|
|||
}
|
||||
}
|
||||
|
||||
func (b *Builder) svcTaggedAddresses(v map[string]ServiceAddress) map[string]structs.ServiceAddress {
|
||||
if len(v) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
svcAddrs := make(map[string]structs.ServiceAddress)
|
||||
for addrName, addrConf := range v {
|
||||
addr := structs.ServiceAddress{}
|
||||
if addrConf.Address != nil {
|
||||
addr.Address = *addrConf.Address
|
||||
}
|
||||
if addrConf.Port != nil {
|
||||
addr.Port = *addrConf.Port
|
||||
}
|
||||
|
||||
svcAddrs[addrName] = addr
|
||||
}
|
||||
return svcAddrs
|
||||
}
|
||||
|
||||
func (b *Builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition {
|
||||
if v == nil {
|
||||
return nil
|
||||
|
@ -1215,6 +1235,7 @@ func (b *Builder) serviceVal(v *ServiceDefinition) *structs.ServiceDefinition {
|
|||
Name: b.stringVal(v.Name),
|
||||
Tags: v.Tags,
|
||||
Address: b.stringVal(v.Address),
|
||||
TaggedAddresses: b.svcTaggedAddresses(v.TaggedAddresses),
|
||||
Meta: meta,
|
||||
Port: b.intVal(v.Port),
|
||||
Token: b.stringVal(v.Token),
|
||||
|
|
|
@ -360,19 +360,25 @@ type ServiceWeights struct {
|
|||
Warning *int `json:"warning,omitempty" hcl:"warning" mapstructure:"warning"`
|
||||
}
|
||||
|
||||
type ServiceAddress struct {
|
||||
Address *string `json:"address,omitempty" hcl:"address" mapstructure:"address"`
|
||||
Port *int `json:"port,omitempty" hcl:"port" mapstructure:"port"`
|
||||
}
|
||||
|
||||
type ServiceDefinition struct {
|
||||
Kind *string `json:"kind,omitempty" hcl:"kind" mapstructure:"kind"`
|
||||
ID *string `json:"id,omitempty" hcl:"id" mapstructure:"id"`
|
||||
Name *string `json:"name,omitempty" hcl:"name" mapstructure:"name"`
|
||||
Tags []string `json:"tags,omitempty" hcl:"tags" mapstructure:"tags"`
|
||||
Address *string `json:"address,omitempty" hcl:"address" mapstructure:"address"`
|
||||
Meta map[string]string `json:"meta,omitempty" hcl:"meta" mapstructure:"meta"`
|
||||
Port *int `json:"port,omitempty" hcl:"port" mapstructure:"port"`
|
||||
Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"`
|
||||
Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
|
||||
Token *string `json:"token,omitempty" hcl:"token" mapstructure:"token"`
|
||||
Weights *ServiceWeights `json:"weights,omitempty" hcl:"weights" mapstructure:"weights"`
|
||||
EnableTagOverride *bool `json:"enable_tag_override,omitempty" hcl:"enable_tag_override" mapstructure:"enable_tag_override"`
|
||||
Kind *string `json:"kind,omitempty" hcl:"kind" mapstructure:"kind"`
|
||||
ID *string `json:"id,omitempty" hcl:"id" mapstructure:"id"`
|
||||
Name *string `json:"name,omitempty" hcl:"name" mapstructure:"name"`
|
||||
Tags []string `json:"tags,omitempty" hcl:"tags" mapstructure:"tags"`
|
||||
Address *string `json:"address,omitempty" hcl:"address" mapstructure:"address"`
|
||||
TaggedAddresses map[string]ServiceAddress `json:"tagged_addresses,omitempty" hcl:"tagged_addresses" mapstructure:"tagged_addresses"`
|
||||
Meta map[string]string `json:"meta,omitempty" hcl:"meta" mapstructure:"meta"`
|
||||
Port *int `json:"port,omitempty" hcl:"port" mapstructure:"port"`
|
||||
Check *CheckDefinition `json:"check,omitempty" hcl:"check" mapstructure:"check"`
|
||||
Checks []CheckDefinition `json:"checks,omitempty" hcl:"checks" mapstructure:"checks"`
|
||||
Token *string `json:"token,omitempty" hcl:"token" mapstructure:"token"`
|
||||
Weights *ServiceWeights `json:"weights,omitempty" hcl:"weights" mapstructure:"weights"`
|
||||
EnableTagOverride *bool `json:"enable_tag_override,omitempty" hcl:"enable_tag_override" mapstructure:"enable_tag_override"`
|
||||
// DEPRECATED (ProxyDestination) - remove this when removing ProxyDestination
|
||||
ProxyDestination *string `json:"proxy_destination,omitempty" hcl:"proxy_destination" mapstructure:"proxy_destination"`
|
||||
Proxy *ServiceProxy `json:"proxy,omitempty" hcl:"proxy" mapstructure:"proxy"`
|
||||
|
|
|
@ -2194,6 +2194,12 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
|
|||
"service": {
|
||||
"name": "a",
|
||||
"port": 80,
|
||||
"tagged_addresses": {
|
||||
"wan": {
|
||||
"address": "198.18.3.4",
|
||||
"port": 443
|
||||
}
|
||||
},
|
||||
"enable_tag_override": true,
|
||||
"check": {
|
||||
"id": "x",
|
||||
|
@ -2210,6 +2216,12 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
|
|||
name = "a"
|
||||
port = 80
|
||||
enable_tag_override = true
|
||||
tagged_addresses = {
|
||||
wan = {
|
||||
address = "198.18.3.4"
|
||||
port = 443
|
||||
}
|
||||
}
|
||||
check = {
|
||||
id = "x"
|
||||
name = "y"
|
||||
|
@ -2222,8 +2234,14 @@ func TestConfigFlagsAndEdgecases(t *testing.T) {
|
|||
patch: func(rt *RuntimeConfig) {
|
||||
rt.Services = []*structs.ServiceDefinition{
|
||||
&structs.ServiceDefinition{
|
||||
Name: "a",
|
||||
Port: 80,
|
||||
Name: "a",
|
||||
Port: 80,
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "198.18.3.4",
|
||||
Port: 443,
|
||||
},
|
||||
},
|
||||
EnableTagOverride: true,
|
||||
Checks: []*structs.CheckType{
|
||||
&structs.CheckType{
|
||||
|
@ -3251,6 +3269,16 @@ func TestFullConfig(t *testing.T) {
|
|||
"meta": {
|
||||
"mymeta": "data"
|
||||
},
|
||||
"tagged_addresses": {
|
||||
"lan": {
|
||||
"address": "2d79888a",
|
||||
"port": 2143
|
||||
},
|
||||
"wan": {
|
||||
"address": "d4db85e2",
|
||||
"port": 6109
|
||||
}
|
||||
},
|
||||
"tags": ["nkwshvM5", "NTDWn3ek"],
|
||||
"address": "cOlSOhbp",
|
||||
"token": "msy7iWER",
|
||||
|
@ -3820,6 +3848,16 @@ func TestFullConfig(t *testing.T) {
|
|||
meta = {
|
||||
mymeta = "data"
|
||||
}
|
||||
tagged_addresses = {
|
||||
lan = {
|
||||
address = "2d79888a"
|
||||
port = 2143
|
||||
}
|
||||
wan = {
|
||||
address = "d4db85e2"
|
||||
port = 6109
|
||||
}
|
||||
}
|
||||
tags = ["nkwshvM5", "NTDWn3ek"]
|
||||
address = "cOlSOhbp"
|
||||
token = "msy7iWER"
|
||||
|
@ -4603,8 +4641,18 @@ func TestFullConfig(t *testing.T) {
|
|||
},
|
||||
},
|
||||
{
|
||||
ID: "dLOXpSCI",
|
||||
Name: "o1ynPkp0",
|
||||
ID: "dLOXpSCI",
|
||||
Name: "o1ynPkp0",
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"lan": structs.ServiceAddress{
|
||||
Address: "2d79888a",
|
||||
Port: 2143,
|
||||
},
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "d4db85e2",
|
||||
Port: 6109,
|
||||
},
|
||||
},
|
||||
Tags: []string{"nkwshvM5", "NTDWn3ek"},
|
||||
Address: "cOlSOhbp",
|
||||
Token: "msy7iWER",
|
||||
|
@ -5287,6 +5335,7 @@ func TestSanitize(t *testing.T) {
|
|||
"Port": 0,
|
||||
"Proxy": null,
|
||||
"ProxyDestination": "",
|
||||
"TaggedAddresses": {},
|
||||
"Tags": [],
|
||||
"Token": "hidden",
|
||||
"Weights": {
|
||||
|
|
10
agent/dns.go
10
agent/dns.go
|
@ -1354,8 +1354,8 @@ func (d *DNSServer) serviceNodeRecords(cfg *dnsConfig, dc string, nodes structs.
|
|||
// Start with the translated address but use the service address,
|
||||
// if specified.
|
||||
addr := d.agent.TranslateAddress(dc, node.Node.Address, node.Node.TaggedAddresses)
|
||||
if node.Service.Address != "" {
|
||||
addr = node.Service.Address
|
||||
if svcAddr := d.agent.TranslateServiceAddress(dc, node.Service.Address, node.Service.TaggedAddresses); svcAddr != "" {
|
||||
addr = svcAddr
|
||||
}
|
||||
|
||||
// If the service address is a CNAME for the service we are looking
|
||||
|
@ -1488,7 +1488,7 @@ func (d *DNSServer) serviceSRVRecords(cfg *dnsConfig, dc string, nodes structs.C
|
|||
},
|
||||
Priority: 1,
|
||||
Weight: uint16(weight),
|
||||
Port: uint16(node.Service.Port),
|
||||
Port: uint16(d.agent.TranslateServicePort(dc, node.Service.Port, node.Service.TaggedAddresses)),
|
||||
Target: fmt.Sprintf("%s.node.%s.%s", node.Node.Node, dc, d.domain),
|
||||
}
|
||||
resp.Answer = append(resp.Answer, srvRec)
|
||||
|
@ -1496,8 +1496,8 @@ func (d *DNSServer) serviceSRVRecords(cfg *dnsConfig, dc string, nodes structs.C
|
|||
// Start with the translated address but use the service address,
|
||||
// if specified.
|
||||
addr := d.agent.TranslateAddress(dc, node.Node.Address, node.Node.TaggedAddresses)
|
||||
if node.Service.Address != "" {
|
||||
addr = node.Service.Address
|
||||
if svcAddr := d.agent.TranslateServiceAddress(dc, node.Service.Address, node.Service.TaggedAddresses); svcAddr != "" {
|
||||
addr = svcAddr
|
||||
}
|
||||
|
||||
// Add the extra record
|
||||
|
|
|
@ -2345,7 +2345,7 @@ func TestDNS_ServiceLookup_ServiceAddressIPV6(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestDNS_ServiceLookup_WanAddress(t *testing.T) {
|
||||
func TestDNS_ServiceLookup_WanTranslation(t *testing.T) {
|
||||
t.Parallel()
|
||||
a1 := NewTestAgent(t, t.Name(), `
|
||||
datacenter = "dc1"
|
||||
|
@ -2363,38 +2363,11 @@ func TestDNS_ServiceLookup_WanAddress(t *testing.T) {
|
|||
|
||||
// Join WAN cluster
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN)
|
||||
if _, err := a2.JoinWAN([]string{addr}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
_, err := a2.JoinWAN([]string{addr})
|
||||
require.NoError(t, err)
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
if got, want := len(a1.WANMembers()), 2; got < want {
|
||||
r.Fatalf("got %d WAN members want at least %d", got, want)
|
||||
}
|
||||
if got, want := len(a2.WANMembers()), 2; got < want {
|
||||
r.Fatalf("got %d WAN members want at least %d", got, want)
|
||||
}
|
||||
})
|
||||
|
||||
// Register a remote node with a service. This is in a retry since we
|
||||
// need the datacenter to have a route which takes a little more time
|
||||
// beyond the join, and we don't have direct access to the router here.
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
args := &structs.RegisterRequest{
|
||||
Datacenter: "dc2",
|
||||
Node: "foo",
|
||||
Address: "127.0.0.1",
|
||||
TaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
Service: &structs.NodeService{
|
||||
Service: "db",
|
||||
},
|
||||
}
|
||||
|
||||
var out struct{}
|
||||
if err := a2.RPC("Catalog.Register", args, &out); err != nil {
|
||||
r.Fatalf("err: %v", err)
|
||||
}
|
||||
require.Len(t, a1.WANMembers(), 2)
|
||||
require.Len(t, a2.WANMembers(), 2)
|
||||
})
|
||||
|
||||
// Register an equivalent prepared query.
|
||||
|
@ -2410,126 +2383,173 @@ func TestDNS_ServiceLookup_WanAddress(t *testing.T) {
|
|||
},
|
||||
},
|
||||
}
|
||||
if err := a2.RPC("PreparedQuery.Apply", args, &id); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
require.NoError(t, a2.RPC("PreparedQuery.Apply", args, &id))
|
||||
}
|
||||
|
||||
// Look up the SRV record via service and prepared query.
|
||||
questions := []string{
|
||||
"db.service.dc2.consul.",
|
||||
id + ".query.dc2.consul.",
|
||||
}
|
||||
for _, question := range questions {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(question, dns.TypeSRV)
|
||||
type testCase struct {
|
||||
nodeTaggedAddresses map[string]string
|
||||
serviceAddress string
|
||||
serviceTaggedAddresses map[string]structs.ServiceAddress
|
||||
|
||||
c := new(dns.Client)
|
||||
dnsAddr string
|
||||
|
||||
addr := a1.config.DNSAddrs[0]
|
||||
in, _, err := c.Exchange(m, addr.String())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(in.Answer) != 1 {
|
||||
t.Fatalf("Bad: %#v", in)
|
||||
}
|
||||
|
||||
aRec, ok := in.Extra[0].(*dns.A)
|
||||
if !ok {
|
||||
t.Fatalf("Bad: %#v", in.Extra[0])
|
||||
}
|
||||
if aRec.Hdr.Name != "7f000002.addr.dc2.consul." {
|
||||
t.Fatalf("Bad: %#v", in.Extra[0])
|
||||
}
|
||||
if aRec.A.String() != "127.0.0.2" {
|
||||
t.Fatalf("Bad: %#v", in.Extra[0])
|
||||
}
|
||||
expectedPort uint16
|
||||
expectedAddress string
|
||||
expectedARRName string
|
||||
}
|
||||
|
||||
// Also check the A record directly
|
||||
for _, question := range questions {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(question, dns.TypeA)
|
||||
|
||||
c := new(dns.Client)
|
||||
addr := a1.config.DNSAddrs[0]
|
||||
in, _, err := c.Exchange(m, addr.String())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
|
||||
if len(in.Answer) != 1 {
|
||||
t.Fatalf("Bad: %#v", in)
|
||||
}
|
||||
|
||||
aRec, ok := in.Answer[0].(*dns.A)
|
||||
if !ok {
|
||||
t.Fatalf("Bad: %#v", in.Answer[0])
|
||||
}
|
||||
if aRec.Hdr.Name != question {
|
||||
t.Fatalf("Bad: %#v", in.Answer[0])
|
||||
}
|
||||
if aRec.A.String() != "127.0.0.2" {
|
||||
t.Fatalf("Bad: %#v", in.Answer[0])
|
||||
}
|
||||
cases := map[string]testCase{
|
||||
"node-addr-from-dc1": testCase{
|
||||
dnsAddr: a1.config.DNSAddrs[0].String(),
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "127.0.0.1",
|
||||
expectedARRName: "foo.node.dc2.consul.",
|
||||
},
|
||||
"node-wan-from-dc1": testCase{
|
||||
dnsAddr: a1.config.DNSAddrs[0].String(),
|
||||
nodeTaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "127.0.0.2",
|
||||
expectedARRName: "7f000002.addr.dc2.consul.",
|
||||
},
|
||||
"service-addr-from-dc1": testCase{
|
||||
dnsAddr: a1.config.DNSAddrs[0].String(),
|
||||
nodeTaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
serviceAddress: "10.0.1.1",
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "10.0.1.1",
|
||||
expectedARRName: "0a000101.addr.dc2.consul.",
|
||||
},
|
||||
"service-wan-from-dc1": testCase{
|
||||
dnsAddr: a1.config.DNSAddrs[0].String(),
|
||||
nodeTaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
serviceAddress: "10.0.1.1",
|
||||
serviceTaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
expectedPort: 80,
|
||||
expectedAddress: "198.18.0.1",
|
||||
expectedARRName: "c6120001.addr.dc2.consul.",
|
||||
},
|
||||
"node-addr-from-dc2": testCase{
|
||||
dnsAddr: a2.config.DNSAddrs[0].String(),
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "127.0.0.1",
|
||||
expectedARRName: "foo.node.dc2.consul.",
|
||||
},
|
||||
"node-wan-from-dc2": testCase{
|
||||
dnsAddr: a2.config.DNSAddrs[0].String(),
|
||||
nodeTaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "127.0.0.1",
|
||||
expectedARRName: "foo.node.dc2.consul.",
|
||||
},
|
||||
"service-addr-from-dc2": testCase{
|
||||
dnsAddr: a2.config.DNSAddrs[0].String(),
|
||||
nodeTaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
serviceAddress: "10.0.1.1",
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "10.0.1.1",
|
||||
expectedARRName: "0a000101.addr.dc2.consul.",
|
||||
},
|
||||
"service-wan-from-dc2": testCase{
|
||||
dnsAddr: a2.config.DNSAddrs[0].String(),
|
||||
nodeTaggedAddresses: map[string]string{
|
||||
"wan": "127.0.0.2",
|
||||
},
|
||||
serviceAddress: "10.0.1.1",
|
||||
serviceTaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
expectedPort: 8080,
|
||||
expectedAddress: "10.0.1.1",
|
||||
expectedARRName: "0a000101.addr.dc2.consul.",
|
||||
},
|
||||
}
|
||||
|
||||
// Now query from the same DC and make sure we get the local address
|
||||
for _, question := range questions {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(question, dns.TypeSRV)
|
||||
for name, tc := range cases {
|
||||
name := name
|
||||
tc := tc
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Register a remote node with a service. This is in a retry since we
|
||||
// need the datacenter to have a route which takes a little more time
|
||||
// beyond the join, and we don't have direct access to the router here.
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
args := &structs.RegisterRequest{
|
||||
Datacenter: "dc2",
|
||||
Node: "foo",
|
||||
Address: "127.0.0.1",
|
||||
TaggedAddresses: tc.nodeTaggedAddresses,
|
||||
Service: &structs.NodeService{
|
||||
Service: "db",
|
||||
Address: tc.serviceAddress,
|
||||
Port: 8080,
|
||||
TaggedAddresses: tc.serviceTaggedAddresses,
|
||||
},
|
||||
}
|
||||
|
||||
c := new(dns.Client)
|
||||
addr := a2.Config.DNSAddrs[0]
|
||||
in, _, err := c.Exchange(m, addr.String())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
var out struct{}
|
||||
require.NoError(t, a2.RPC("Catalog.Register", args, &out))
|
||||
})
|
||||
|
||||
if len(in.Answer) != 1 {
|
||||
t.Fatalf("Bad: %#v", in)
|
||||
}
|
||||
// Look up the SRV record via service and prepared query.
|
||||
questions := []string{
|
||||
"db.service.dc2.consul.",
|
||||
id + ".query.dc2.consul.",
|
||||
}
|
||||
for _, question := range questions {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(question, dns.TypeSRV)
|
||||
|
||||
aRec, ok := in.Extra[0].(*dns.A)
|
||||
if !ok {
|
||||
t.Fatalf("Bad: %#v", in.Extra[0])
|
||||
}
|
||||
if aRec.Hdr.Name != "foo.node.dc2.consul." {
|
||||
t.Fatalf("Bad: %#v", in.Extra[0])
|
||||
}
|
||||
if aRec.A.String() != "127.0.0.1" {
|
||||
t.Fatalf("Bad: %#v", in.Extra[0])
|
||||
}
|
||||
}
|
||||
c := new(dns.Client)
|
||||
|
||||
// Also check the A record directly from DC2
|
||||
for _, question := range questions {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(question, dns.TypeA)
|
||||
addr := tc.dnsAddr
|
||||
in, _, err := c.Exchange(m, addr)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, in.Answer, 1)
|
||||
srvRec, ok := in.Answer[0].(*dns.SRV)
|
||||
require.True(t, ok, "Bad: %#v", in.Answer[0])
|
||||
require.Equal(t, tc.expectedPort, srvRec.Port)
|
||||
|
||||
c := new(dns.Client)
|
||||
addr := a2.Config.DNSAddrs[0]
|
||||
in, _, err := c.Exchange(m, addr.String())
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
aRec, ok := in.Extra[0].(*dns.A)
|
||||
require.True(t, ok, "Bad: %#v", in.Extra[0])
|
||||
require.Equal(t, tc.expectedARRName, aRec.Hdr.Name)
|
||||
require.Equal(t, tc.expectedAddress, aRec.A.String())
|
||||
}
|
||||
|
||||
if len(in.Answer) != 1 {
|
||||
t.Fatalf("Bad: %#v", in)
|
||||
}
|
||||
// Also check the A record directly
|
||||
for _, question := range questions {
|
||||
m := new(dns.Msg)
|
||||
m.SetQuestion(question, dns.TypeA)
|
||||
|
||||
aRec, ok := in.Answer[0].(*dns.A)
|
||||
if !ok {
|
||||
t.Fatalf("Bad: %#v", in.Answer[0])
|
||||
}
|
||||
if aRec.Hdr.Name != question {
|
||||
t.Fatalf("Bad: %#v", in.Answer[0])
|
||||
}
|
||||
if aRec.A.String() != "127.0.0.1" {
|
||||
t.Fatalf("Bad: %#v", in.Answer[0])
|
||||
}
|
||||
c := new(dns.Client)
|
||||
addr := tc.dnsAddr
|
||||
in, _, err := c.Exchange(m, addr)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, in.Answer, 1)
|
||||
|
||||
aRec, ok := in.Answer[0].(*dns.A)
|
||||
require.True(t, ok, "Bad: %#v", in.Answer[0])
|
||||
require.Equal(t, question, aRec.Hdr.Name)
|
||||
require.Equal(t, tc.expectedAddress, aRec.A.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -977,7 +977,6 @@ func TestHealthServiceNodes_WanTranslation(t *testing.T) {
|
|||
acl_datacenter = ""
|
||||
`)
|
||||
defer a1.Shutdown()
|
||||
testrpc.WaitForLeader(t, a1.RPC, "dc1")
|
||||
|
||||
a2 := NewTestAgent(t, t.Name(), `
|
||||
datacenter = "dc2"
|
||||
|
@ -985,17 +984,13 @@ func TestHealthServiceNodes_WanTranslation(t *testing.T) {
|
|||
acl_datacenter = ""
|
||||
`)
|
||||
defer a2.Shutdown()
|
||||
testrpc.WaitForLeader(t, a2.RPC, "dc2")
|
||||
|
||||
// Wait for the WAN join.
|
||||
addr := fmt.Sprintf("127.0.0.1:%d", a1.Config.SerfPortWAN)
|
||||
if _, err := a2.JoinWAN([]string{addr}); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
_, err := a2.srv.agent.JoinWAN([]string{addr})
|
||||
require.NoError(t, err)
|
||||
retry.Run(t, func(r *retry.R) {
|
||||
if got, want := len(a1.WANMembers()), 2; got < want {
|
||||
r.Fatalf("got %d WAN members want at least %d", got, want)
|
||||
}
|
||||
require.Len(r, a1.WANMembers(), 2)
|
||||
})
|
||||
|
||||
// Register a node with DC2.
|
||||
|
@ -1009,51 +1004,55 @@ func TestHealthServiceNodes_WanTranslation(t *testing.T) {
|
|||
},
|
||||
Service: &structs.NodeService{
|
||||
Service: "http_wan_translation_test",
|
||||
Address: "127.0.0.1",
|
||||
Port: 8080,
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "1.2.3.4",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
var out struct{}
|
||||
if err := a2.RPC("Catalog.Register", args, &out); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
require.NoError(t, a2.RPC("Catalog.Register", args, &out))
|
||||
}
|
||||
|
||||
// Query for a service in DC2 from DC1.
|
||||
req, _ := http.NewRequest("GET", "/v1/health/service/http_wan_translation_test?dc=dc2", nil)
|
||||
resp1 := httptest.NewRecorder()
|
||||
obj1, err1 := a1.srv.HealthServiceNodes(resp1, req)
|
||||
if err1 != nil {
|
||||
t.Fatalf("err: %v", err1)
|
||||
}
|
||||
assertIndex(t, resp1)
|
||||
require.NoError(t, err1)
|
||||
require.NoError(t, checkIndex(resp1))
|
||||
|
||||
// Expect that DC1 gives us a WAN address (since the node is in DC2).
|
||||
nodes1 := obj1.(structs.CheckServiceNodes)
|
||||
if len(nodes1) != 1 {
|
||||
t.Fatalf("bad: %v", obj1)
|
||||
}
|
||||
node1 := nodes1[0].Node
|
||||
if node1.Address != "127.0.0.2" {
|
||||
t.Fatalf("bad: %v", node1)
|
||||
}
|
||||
nodes1, ok := obj1.(structs.CheckServiceNodes)
|
||||
require.True(t, ok, "obj1 is not a structs.CheckServiceNodes")
|
||||
require.Len(t, nodes1, 1)
|
||||
node1 := nodes1[0]
|
||||
require.NotNil(t, node1.Node)
|
||||
require.Equal(t, node1.Node.Address, "127.0.0.2")
|
||||
require.NotNil(t, node1.Service)
|
||||
require.Equal(t, node1.Service.Address, "1.2.3.4")
|
||||
require.Equal(t, node1.Service.Port, 80)
|
||||
|
||||
// Query DC2 from DC2.
|
||||
resp2 := httptest.NewRecorder()
|
||||
obj2, err2 := a2.srv.HealthServiceNodes(resp2, req)
|
||||
if err2 != nil {
|
||||
t.Fatalf("err: %v", err2)
|
||||
}
|
||||
assertIndex(t, resp2)
|
||||
require.NoError(t, err2)
|
||||
require.NoError(t, checkIndex(resp2))
|
||||
|
||||
// Expect that DC2 gives us a private address (since the node is in DC2).
|
||||
nodes2 := obj2.(structs.CheckServiceNodes)
|
||||
if len(nodes2) != 1 {
|
||||
t.Fatalf("bad: %v", obj2)
|
||||
}
|
||||
node2 := nodes2[0].Node
|
||||
if node2.Address != "127.0.0.1" {
|
||||
t.Fatalf("bad: %v", node2)
|
||||
}
|
||||
// Expect that DC2 gives us a local address (since the node is in DC2).
|
||||
nodes2, ok := obj2.(structs.CheckServiceNodes)
|
||||
require.True(t, ok, "obj2 is not a structs.ServiceNodes")
|
||||
require.Len(t, nodes2, 1)
|
||||
node2 := nodes2[0]
|
||||
require.NotNil(t, node2.Node)
|
||||
require.Equal(t, node2.Node.Address, "127.0.0.1")
|
||||
require.NotNil(t, node2.Service)
|
||||
require.Equal(t, node2.Service.Address, "127.0.0.1")
|
||||
require.Equal(t, node2.Service.Port, 8080)
|
||||
}
|
||||
|
||||
func TestHealthConnectServiceNodes(t *testing.T) {
|
||||
|
|
|
@ -514,37 +514,41 @@ func TestPreparedQuery_Execute(t *testing.T) {
|
|||
"wan": "127.0.0.2",
|
||||
},
|
||||
}
|
||||
nodesResponse[0].Service = &structs.NodeService{
|
||||
Service: "foo",
|
||||
Address: "10.0.1.1",
|
||||
Port: 8080,
|
||||
TaggedAddresses: map[string]structs.ServiceAddress{
|
||||
"wan": structs.ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
}
|
||||
reply.Nodes = nodesResponse
|
||||
reply.Datacenter = "dc2"
|
||||
return nil
|
||||
},
|
||||
}
|
||||
if err := a.registerEndpoint("PreparedQuery", &m); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
require.NoError(t, a.registerEndpoint("PreparedQuery", &m))
|
||||
|
||||
body := bytes.NewBuffer(nil)
|
||||
req, _ := http.NewRequest("GET", "/v1/query/my-id/execute?dc=dc2", body)
|
||||
resp := httptest.NewRecorder()
|
||||
obj, err := a.srv.PreparedQuerySpecific(resp, req)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp.Code != 200 {
|
||||
t.Fatalf("bad code: %d", resp.Code)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, 200, resp.Code)
|
||||
r, ok := obj.(structs.PreparedQueryExecuteResponse)
|
||||
if !ok {
|
||||
t.Fatalf("unexpected: %T", obj)
|
||||
}
|
||||
if r.Nodes == nil || len(r.Nodes) != 1 {
|
||||
t.Fatalf("bad: %v", r)
|
||||
}
|
||||
require.True(t, ok, "unexpected: %T", obj)
|
||||
require.NotNil(t, r.Nodes)
|
||||
require.Len(t, r.Nodes, 1)
|
||||
|
||||
node := r.Nodes[0]
|
||||
if node.Node.Address != "127.0.0.2" {
|
||||
t.Fatalf("bad: %v", node.Node)
|
||||
}
|
||||
require.NotNil(t, node.Node)
|
||||
require.Equal(t, "127.0.0.2", node.Node.Address)
|
||||
require.NotNil(t, node.Service)
|
||||
require.Equal(t, "198.18.0.1", node.Service.Address)
|
||||
require.Equal(t, 80, node.Service.Port)
|
||||
})
|
||||
|
||||
// Ensure WAN translation doesn't occur for the local DC.
|
||||
|
|
|
@ -19,6 +19,7 @@ type ServiceDefinition struct {
|
|||
Name string
|
||||
Tags []string
|
||||
Address string
|
||||
TaggedAddresses map[string]ServiceAddress
|
||||
Meta map[string]string
|
||||
Port int
|
||||
Check CheckType
|
||||
|
@ -76,6 +77,14 @@ func (s *ServiceDefinition) NodeService() *NodeService {
|
|||
if ns.ID == "" && ns.Service != "" {
|
||||
ns.ID = ns.Service
|
||||
}
|
||||
if len(s.TaggedAddresses) > 0 {
|
||||
taggedAddrs := make(map[string]ServiceAddress)
|
||||
for k, v := range s.TaggedAddresses {
|
||||
taggedAddrs[k] = v
|
||||
}
|
||||
|
||||
ns.TaggedAddresses = taggedAddrs
|
||||
}
|
||||
return ns
|
||||
}
|
||||
|
||||
|
|
|
@ -591,6 +591,7 @@ type ServiceNode struct {
|
|||
ServiceName string
|
||||
ServiceTags []string
|
||||
ServiceAddress string
|
||||
ServiceTaggedAddresses map[string]ServiceAddress `json:",omitempty"`
|
||||
ServiceWeights Weights
|
||||
ServiceMeta map[string]string
|
||||
ServicePort int
|
||||
|
@ -613,6 +614,14 @@ func (s *ServiceNode) PartialClone() *ServiceNode {
|
|||
nsmeta[k] = v
|
||||
}
|
||||
|
||||
var svcTaggedAddrs map[string]ServiceAddress
|
||||
if len(s.ServiceTaggedAddresses) > 0 {
|
||||
svcTaggedAddrs = make(map[string]ServiceAddress)
|
||||
for k, v := range s.ServiceTaggedAddresses {
|
||||
svcTaggedAddrs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return &ServiceNode{
|
||||
// Skip ID, see above.
|
||||
Node: s.Node,
|
||||
|
@ -623,6 +632,7 @@ func (s *ServiceNode) PartialClone() *ServiceNode {
|
|||
ServiceName: s.ServiceName,
|
||||
ServiceTags: tags,
|
||||
ServiceAddress: s.ServiceAddress,
|
||||
ServiceTaggedAddresses: svcTaggedAddrs,
|
||||
ServicePort: s.ServicePort,
|
||||
ServiceMeta: nsmeta,
|
||||
ServiceWeights: s.ServiceWeights,
|
||||
|
@ -646,6 +656,7 @@ func (s *ServiceNode) ToNodeService() *NodeService {
|
|||
Service: s.ServiceName,
|
||||
Tags: s.ServiceTags,
|
||||
Address: s.ServiceAddress,
|
||||
TaggedAddresses: s.ServiceTaggedAddresses,
|
||||
Port: s.ServicePort,
|
||||
Meta: s.ServiceMeta,
|
||||
Weights: &s.ServiceWeights,
|
||||
|
@ -683,6 +694,16 @@ const (
|
|||
ServiceKindConnectProxy ServiceKind = "connect-proxy"
|
||||
)
|
||||
|
||||
// Type to hold a address and port of a service
|
||||
type ServiceAddress struct {
|
||||
Address string
|
||||
Port int
|
||||
}
|
||||
|
||||
func (a ServiceAddress) ToAPIServiceAddress() api.ServiceAddress {
|
||||
return api.ServiceAddress{Address: a.Address, Port: a.Port}
|
||||
}
|
||||
|
||||
// NodeService is a service provided by a node
|
||||
type NodeService struct {
|
||||
// Kind is the kind of service this is. Different kinds of services may
|
||||
|
@ -694,6 +715,7 @@ type NodeService struct {
|
|||
Service string
|
||||
Tags []string
|
||||
Address string
|
||||
TaggedAddresses map[string]ServiceAddress `json:",omitempty"`
|
||||
Meta map[string]string
|
||||
Port int
|
||||
Weights *Weights
|
||||
|
@ -844,6 +866,7 @@ func (s *NodeService) IsSame(other *NodeService) bool {
|
|||
!reflect.DeepEqual(s.Tags, other.Tags) ||
|
||||
s.Address != other.Address ||
|
||||
s.Port != other.Port ||
|
||||
!reflect.DeepEqual(s.TaggedAddresses, other.TaggedAddresses) ||
|
||||
!reflect.DeepEqual(s.Weights, other.Weights) ||
|
||||
!reflect.DeepEqual(s.Meta, other.Meta) ||
|
||||
s.EnableTagOverride != other.EnableTagOverride ||
|
||||
|
@ -876,6 +899,7 @@ func (s *ServiceNode) IsSameService(other *ServiceNode) bool {
|
|||
s.ServiceName != other.ServiceName ||
|
||||
!reflect.DeepEqual(s.ServiceTags, other.ServiceTags) ||
|
||||
s.ServiceAddress != other.ServiceAddress ||
|
||||
!reflect.DeepEqual(s.ServiceTaggedAddresses, other.ServiceTaggedAddresses) ||
|
||||
s.ServicePort != other.ServicePort ||
|
||||
!reflect.DeepEqual(s.ServiceMeta, other.ServiceMeta) ||
|
||||
!reflect.DeepEqual(s.ServiceWeights, other.ServiceWeights) ||
|
||||
|
@ -915,6 +939,7 @@ func (s *NodeService) ToServiceNode(node string) *ServiceNode {
|
|||
ServiceName: s.Service,
|
||||
ServiceTags: s.Tags,
|
||||
ServiceAddress: s.Address,
|
||||
ServiceTaggedAddresses: s.TaggedAddresses,
|
||||
ServicePort: s.Port,
|
||||
ServiceMeta: s.Meta,
|
||||
ServiceWeights: theWeights,
|
||||
|
|
|
@ -162,6 +162,19 @@ var expectedFieldConfigNode bexpr.FieldConfigurations = bexpr.FieldConfiguration
|
|||
},
|
||||
}
|
||||
|
||||
var expectedFieldConfigMapStringServiceAddress bexpr.FieldConfigurations = bexpr.FieldConfigurations{
|
||||
"Address": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "Address",
|
||||
CoerceFn: bexpr.CoerceString,
|
||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual},
|
||||
},
|
||||
"Port": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "Port",
|
||||
CoerceFn: bexpr.CoerceInt,
|
||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual},
|
||||
},
|
||||
}
|
||||
|
||||
var expectedFieldConfigNodeService bexpr.FieldConfigurations = bexpr.FieldConfigurations{
|
||||
"Kind": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "Kind",
|
||||
|
@ -188,6 +201,16 @@ var expectedFieldConfigNodeService bexpr.FieldConfigurations = bexpr.FieldConfig
|
|||
CoerceFn: bexpr.CoerceString,
|
||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual},
|
||||
},
|
||||
"TaggedAddresses": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "TaggedAddresses",
|
||||
CoerceFn: bexpr.CoerceString,
|
||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchIsEmpty, bexpr.MatchIsNotEmpty, bexpr.MatchIn, bexpr.MatchNotIn},
|
||||
SubFields: bexpr.FieldConfigurations{
|
||||
bexpr.FieldNameAny: &bexpr.FieldConfiguration{
|
||||
SubFields: expectedFieldConfigMapStringServiceAddress,
|
||||
},
|
||||
},
|
||||
},
|
||||
"Meta": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "Meta",
|
||||
CoerceFn: bexpr.CoerceString,
|
||||
|
@ -276,6 +299,16 @@ var expectedFieldConfigServiceNode bexpr.FieldConfigurations = bexpr.FieldConfig
|
|||
CoerceFn: bexpr.CoerceString,
|
||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchEqual, bexpr.MatchNotEqual},
|
||||
},
|
||||
"ServiceTaggedAddresses": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "ServiceTaggedAddresses",
|
||||
CoerceFn: bexpr.CoerceString,
|
||||
SupportedOperations: []bexpr.MatchOperator{bexpr.MatchIsEmpty, bexpr.MatchIsNotEmpty, bexpr.MatchIn, bexpr.MatchNotIn},
|
||||
SubFields: bexpr.FieldConfigurations{
|
||||
bexpr.FieldNameAny: &bexpr.FieldConfiguration{
|
||||
SubFields: expectedFieldConfigMapStringServiceAddress,
|
||||
},
|
||||
},
|
||||
},
|
||||
"ServiceMeta": &bexpr.FieldConfiguration{
|
||||
StructFieldName: "ServiceMeta",
|
||||
CoerceFn: bexpr.CoerceString,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package structs
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -144,7 +145,17 @@ func testServiceNode(t *testing.T) *ServiceNode {
|
|||
ServiceName: "dogs",
|
||||
ServiceTags: []string{"prod", "v1"},
|
||||
ServiceAddress: "127.0.0.2",
|
||||
ServicePort: 8080,
|
||||
ServiceTaggedAddresses: map[string]ServiceAddress{
|
||||
"lan": ServiceAddress{
|
||||
Address: "127.0.0.2",
|
||||
Port: 8080,
|
||||
},
|
||||
"wan": ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
ServicePort: 8080,
|
||||
ServiceMeta: map[string]string{
|
||||
"service": "metadata",
|
||||
},
|
||||
|
@ -241,6 +252,7 @@ func TestStructs_ServiceNode_IsSameService(t *testing.T) {
|
|||
serviceProxyDestination := sn.ServiceProxyDestination
|
||||
serviceProxy := sn.ServiceProxy
|
||||
serviceConnect := sn.ServiceConnect
|
||||
serviceTaggedAddresses := sn.ServiceTaggedAddresses
|
||||
|
||||
n := sn.ToNodeService().ToServiceNode(node)
|
||||
other := sn.ToNodeService().ToServiceNode(node)
|
||||
|
@ -275,6 +287,7 @@ func TestStructs_ServiceNode_IsSameService(t *testing.T) {
|
|||
check(func() { other.ServiceWeights = Weights{Passing: 42, Warning: 41} }, func() { other.ServiceWeights = serviceWeights })
|
||||
check(func() { other.ServiceProxy = ConnectProxyConfig{} }, func() { other.ServiceProxy = serviceProxy })
|
||||
check(func() { other.ServiceConnect = ServiceConnect{} }, func() { other.ServiceConnect = serviceConnect })
|
||||
check(func() { other.ServiceTaggedAddresses = nil }, func() { other.ServiceTaggedAddresses = serviceTaggedAddresses })
|
||||
}
|
||||
|
||||
func TestStructs_ServiceNode_PartialClone(t *testing.T) {
|
||||
|
@ -321,6 +334,10 @@ func TestStructs_ServiceNode_PartialClone(t *testing.T) {
|
|||
if reflect.DeepEqual(sn, clone) {
|
||||
t.Fatalf("clone wasn't independent of the original for Meta")
|
||||
}
|
||||
|
||||
// ensure that the tagged addresses were copied and not just a pointer to the map
|
||||
sn.ServiceTaggedAddresses["foo"] = ServiceAddress{Address: "consul.is.awesome", Port: 443}
|
||||
require.NotEqual(t, sn, clone)
|
||||
}
|
||||
|
||||
func TestStructs_ServiceNode_Conversions(t *testing.T) {
|
||||
|
@ -472,6 +489,16 @@ func TestStructs_NodeService_IsSame(t *testing.T) {
|
|||
Service: "theservice",
|
||||
Tags: []string{"foo", "bar"},
|
||||
Address: "127.0.0.1",
|
||||
TaggedAddresses: map[string]ServiceAddress{
|
||||
"lan": ServiceAddress{
|
||||
Address: "127.0.0.1",
|
||||
Port: 3456,
|
||||
},
|
||||
"wan": ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 1234,
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
"meta1": "value1",
|
||||
"meta2": "value2",
|
||||
|
@ -497,6 +524,16 @@ func TestStructs_NodeService_IsSame(t *testing.T) {
|
|||
Address: "127.0.0.1",
|
||||
Port: 1234,
|
||||
EnableTagOverride: true,
|
||||
TaggedAddresses: map[string]ServiceAddress{
|
||||
"wan": ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 1234,
|
||||
},
|
||||
"lan": ServiceAddress{
|
||||
Address: "127.0.0.1",
|
||||
Port: 3456,
|
||||
},
|
||||
},
|
||||
Meta: map[string]string{
|
||||
// We don't care about order
|
||||
"meta2": "value2",
|
||||
|
@ -559,6 +596,7 @@ func TestStructs_NodeService_IsSame(t *testing.T) {
|
|||
if !otherServiceNode.IsSameService(otherServiceNodeCopy2) {
|
||||
t.Fatalf("copy should be the same, but was\n %#v\nVS\n %#v", otherServiceNode, otherServiceNodeCopy2)
|
||||
}
|
||||
check(func() { other.TaggedAddresses["lan"] = ServiceAddress{Address: "127.0.0.1", Port: 9999} }, func() { other.TaggedAddresses["lan"] = ServiceAddress{Address: "127.0.0.1", Port: 3456} })
|
||||
}
|
||||
|
||||
func TestStructs_HealthCheck_IsSame(t *testing.T) {
|
||||
|
@ -1045,3 +1083,75 @@ func TestSpecificServiceRequest_CacheInfo(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNodeService_JSON_OmitTaggedAdddresses(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
ns NodeService
|
||||
}{
|
||||
{
|
||||
"nil",
|
||||
NodeService{
|
||||
TaggedAddresses: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty",
|
||||
NodeService{
|
||||
TaggedAddresses: make(map[string]ServiceAddress),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
name := tc.name
|
||||
ns := tc.ns
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
data, err := json.Marshal(ns)
|
||||
require.NoError(t, err)
|
||||
var raw map[string]interface{}
|
||||
err = json.Unmarshal(data, &raw)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, raw, "TaggedAddresses")
|
||||
require.NotContains(t, raw, "tagged_addresses")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestServiceNode_JSON_OmitServiceTaggedAdddresses(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
sn ServiceNode
|
||||
}{
|
||||
{
|
||||
"nil",
|
||||
ServiceNode{
|
||||
ServiceTaggedAddresses: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty",
|
||||
ServiceNode{
|
||||
ServiceTaggedAddresses: make(map[string]ServiceAddress),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
name := tc.name
|
||||
sn := tc.sn
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
data, err := json.Marshal(sn)
|
||||
require.NoError(t, err)
|
||||
var raw map[string]interface{}
|
||||
err = json.Unmarshal(data, &raw)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, raw, "ServiceTaggedAddresses")
|
||||
require.NotContains(t, raw, "service_tagged_addresses")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,30 @@ import (
|
|||
"github.com/hashicorp/consul/agent/structs"
|
||||
)
|
||||
|
||||
// TranslateServicePort is used to provide the final, translated port for a service,
|
||||
// depending on how the agent and the other node are configured. The dc
|
||||
// parameter is the dc the datacenter this node is from.
|
||||
func (a *Agent) TranslateServicePort(dc string, port int, taggedAddresses map[string]structs.ServiceAddress) int {
|
||||
if a.config.TranslateWANAddrs && (a.config.Datacenter != dc) {
|
||||
if wanAddr, ok := taggedAddresses["wan"]; ok && wanAddr.Port != 0 {
|
||||
return wanAddr.Port
|
||||
}
|
||||
}
|
||||
return port
|
||||
}
|
||||
|
||||
// TranslateServiceAddress is used to provide the final, translated address for a node,
|
||||
// depending on how the agent and the other node are configured. The dc
|
||||
// parameter is the dc the datacenter this node is from.
|
||||
func (a *Agent) TranslateServiceAddress(dc string, addr string, taggedAddresses map[string]structs.ServiceAddress) string {
|
||||
if a.config.TranslateWANAddrs && (a.config.Datacenter != dc) {
|
||||
if wanAddr, ok := taggedAddresses["wan"]; ok && wanAddr.Address != "" {
|
||||
return wanAddr.Address
|
||||
}
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
// TranslateAddress is used to provide the final, translated address for a node,
|
||||
// depending on how the agent and the other node are configured. The dc
|
||||
// parameter is the dc the datacenter this node is from.
|
||||
|
@ -45,6 +69,8 @@ func (a *Agent) TranslateAddresses(dc string, subj interface{}) {
|
|||
case structs.CheckServiceNodes:
|
||||
for _, entry := range v {
|
||||
entry.Node.Address = a.TranslateAddress(dc, entry.Node.Address, entry.Node.TaggedAddresses)
|
||||
entry.Service.Address = a.TranslateServiceAddress(dc, entry.Service.Address, entry.Service.TaggedAddresses)
|
||||
entry.Service.Port = a.TranslateServicePort(dc, entry.Service.Port, entry.Service.TaggedAddresses)
|
||||
}
|
||||
case *structs.Node:
|
||||
v.Address = a.TranslateAddress(dc, v.Address, v.TaggedAddresses)
|
||||
|
@ -55,6 +81,16 @@ func (a *Agent) TranslateAddresses(dc string, subj interface{}) {
|
|||
case structs.ServiceNodes:
|
||||
for _, entry := range v {
|
||||
entry.Address = a.TranslateAddress(dc, entry.Address, entry.TaggedAddresses)
|
||||
entry.ServiceAddress = a.TranslateServiceAddress(dc, entry.ServiceAddress, entry.ServiceTaggedAddresses)
|
||||
entry.ServicePort = a.TranslateServicePort(dc, entry.ServicePort, entry.ServiceTaggedAddresses)
|
||||
}
|
||||
case *structs.NodeServices:
|
||||
if v.Node != nil {
|
||||
v.Node.Address = a.TranslateAddress(dc, v.Node.Address, v.Node.TaggedAddresses)
|
||||
}
|
||||
for _, entry := range v.Services {
|
||||
entry.Address = a.TranslateServiceAddress(dc, entry.Address, entry.TaggedAddresses)
|
||||
entry.Port = a.TranslateServicePort(dc, entry.Port, entry.TaggedAddresses)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("Unhandled type passed to address translator: %#v", subj))
|
||||
|
|
20
api/agent.go
20
api/agent.go
|
@ -82,6 +82,7 @@ type AgentService struct {
|
|||
Meta map[string]string
|
||||
Port int
|
||||
Address string
|
||||
TaggedAddresses map[string]ServiceAddress `json:",omitempty"`
|
||||
Weights AgentWeights
|
||||
EnableTagOverride bool
|
||||
CreateIndex uint64 `json:",omitempty" bexpr:"-"`
|
||||
|
@ -157,15 +158,16 @@ type MembersOpts struct {
|
|||
|
||||
// AgentServiceRegistration is used to register a new service
|
||||
type AgentServiceRegistration struct {
|
||||
Kind ServiceKind `json:",omitempty"`
|
||||
ID string `json:",omitempty"`
|
||||
Name string `json:",omitempty"`
|
||||
Tags []string `json:",omitempty"`
|
||||
Port int `json:",omitempty"`
|
||||
Address string `json:",omitempty"`
|
||||
EnableTagOverride bool `json:",omitempty"`
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
Weights *AgentWeights `json:",omitempty"`
|
||||
Kind ServiceKind `json:",omitempty"`
|
||||
ID string `json:",omitempty"`
|
||||
Name string `json:",omitempty"`
|
||||
Tags []string `json:",omitempty"`
|
||||
Port int `json:",omitempty"`
|
||||
Address string `json:",omitempty"`
|
||||
TaggedAddresses map[string]ServiceAddress `json:",omitempty"`
|
||||
EnableTagOverride bool `json:",omitempty"`
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
Weights *AgentWeights `json:",omitempty"`
|
||||
Check *AgentServiceCheck
|
||||
Checks AgentServiceChecks
|
||||
// DEPRECATED (ProxyDestination) - remove this field
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
@ -179,6 +180,12 @@ func TestAPI_AgentServices(t *testing.T) {
|
|||
Name: "foo",
|
||||
ID: "foo",
|
||||
Tags: []string{"bar", "baz"},
|
||||
TaggedAddresses: map[string]ServiceAddress{
|
||||
"lan": ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
Port: 8000,
|
||||
Check: &AgentServiceCheck{
|
||||
TTL: "15s",
|
||||
|
@ -605,6 +612,16 @@ func TestAPI_AgentServiceAddress(t *testing.T) {
|
|||
reg2 := &AgentServiceRegistration{
|
||||
Name: "foo2",
|
||||
Port: 8000,
|
||||
TaggedAddresses: map[string]ServiceAddress{
|
||||
"lan": ServiceAddress{
|
||||
Address: "192.168.0.43",
|
||||
Port: 8000,
|
||||
},
|
||||
"wan": ServiceAddress{
|
||||
Address: "198.18.0.1",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
}
|
||||
if err := agent.ServiceRegister(reg1); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
@ -631,6 +648,13 @@ func TestAPI_AgentServiceAddress(t *testing.T) {
|
|||
if services["foo2"].Address != "" {
|
||||
t.Fatalf("missing Address field in service foo2: %v", services)
|
||||
}
|
||||
require.NotNil(t, services["foo2"].TaggedAddresses)
|
||||
require.Contains(t, services["foo2"].TaggedAddresses, "lan")
|
||||
require.Contains(t, services["foo2"].TaggedAddresses, "wan")
|
||||
require.Equal(t, services["foo2"].TaggedAddresses["lan"].Address, "192.168.0.43")
|
||||
require.Equal(t, services["foo2"].TaggedAddresses["lan"].Port, 8000)
|
||||
require.Equal(t, services["foo2"].TaggedAddresses["wan"].Address, "198.18.0.1")
|
||||
require.Equal(t, services["foo2"].TaggedAddresses["wan"].Port, 80)
|
||||
|
||||
if err := agent.ServiceDeregister("foo"); err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
|
@ -1662,3 +1686,39 @@ func TestAPI_AgentHealthService(t *testing.T) {
|
|||
require.Nil(t, err)
|
||||
requireServiceHealthName(t, testServiceName, HealthPassing, true)
|
||||
}
|
||||
|
||||
func TestAgentService_JSON_OmitTaggedAdddresses(t *testing.T) {
|
||||
t.Parallel()
|
||||
cases := []struct {
|
||||
name string
|
||||
as AgentService
|
||||
}{
|
||||
{
|
||||
"nil",
|
||||
AgentService{
|
||||
TaggedAddresses: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty",
|
||||
AgentService{
|
||||
TaggedAddresses: make(map[string]ServiceAddress),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
name := tc.name
|
||||
as := tc.as
|
||||
t.Run(name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
data, err := json.Marshal(as)
|
||||
require.NoError(t, err)
|
||||
var raw map[string]interface{}
|
||||
err = json.Unmarshal(data, &raw)
|
||||
require.NoError(t, err)
|
||||
require.NotContains(t, raw, "TaggedAddresses")
|
||||
require.NotContains(t, raw, "tagged_addresses")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
package api
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Weights struct {
|
||||
Passing int
|
||||
Warning int
|
||||
|
@ -16,6 +21,11 @@ type Node struct {
|
|||
ModifyIndex uint64
|
||||
}
|
||||
|
||||
type ServiceAddress struct {
|
||||
Address string
|
||||
Port int
|
||||
}
|
||||
|
||||
type CatalogService struct {
|
||||
ID string
|
||||
Node string
|
||||
|
@ -26,6 +36,7 @@ type CatalogService struct {
|
|||
ServiceID string
|
||||
ServiceName string
|
||||
ServiceAddress string
|
||||
ServiceTaggedAddresses map[string]ServiceAddress
|
||||
ServiceTags []string
|
||||
ServiceMeta map[string]string
|
||||
ServicePort int
|
||||
|
@ -242,3 +253,12 @@ func (c *Catalog) Node(node string, q *QueryOptions) (*CatalogNode, *QueryMeta,
|
|||
}
|
||||
return out, qm, nil
|
||||
}
|
||||
|
||||
func ParseServiceAddr(addrPort string) (ServiceAddress, error) {
|
||||
port := 0
|
||||
host, portStr, err := net.SplitHostPort(addrPort)
|
||||
if err == nil {
|
||||
port, err = strconv.Atoi(portStr)
|
||||
}
|
||||
return ServiceAddress{Address: host, Port: port}, err
|
||||
}
|
||||
|
|
|
@ -23,12 +23,13 @@ type cmd struct {
|
|||
help string
|
||||
|
||||
// flags
|
||||
flagId string
|
||||
flagName string
|
||||
flagAddress string
|
||||
flagPort int
|
||||
flagTags []string
|
||||
flagMeta map[string]string
|
||||
flagId string
|
||||
flagName string
|
||||
flagAddress string
|
||||
flagPort int
|
||||
flagTags []string
|
||||
flagMeta map[string]string
|
||||
flagTaggedAddresses map[string]string
|
||||
}
|
||||
|
||||
func (c *cmd) init() {
|
||||
|
@ -43,11 +44,14 @@ func (c *cmd) init() {
|
|||
c.flags.IntVar(&c.flagPort, "port", 0,
|
||||
"Port of the service to register for arg-based registration.")
|
||||
c.flags.Var((*flags.FlagMapValue)(&c.flagMeta), "meta",
|
||||
"Metadata to set on the intention, formatted as key=value. This flag "+
|
||||
"Metadata to set on the service, formatted as key=value. This flag "+
|
||||
"may be specified multiple times to set multiple meta fields.")
|
||||
c.flags.Var((*flags.AppendSliceValue)(&c.flagTags), "tag",
|
||||
"Tag to add to the service. This flag can be specified multiple "+
|
||||
"times to set multiple tags.")
|
||||
c.flags.Var((*flags.FlagMapValue)(&c.flagTaggedAddresses), "tagged-address",
|
||||
"Tagged address to set on the service, formatted as key=value. This flag "+
|
||||
"may be specified multiple times to set multiple addresses.")
|
||||
|
||||
c.http = &flags.HTTPFlags{}
|
||||
flags.Merge(c.flags, c.http.ClientFlags())
|
||||
|
@ -60,13 +64,27 @@ func (c *cmd) Run(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
var taggedAddrs map[string]api.ServiceAddress
|
||||
if len(c.flagTaggedAddresses) > 0 {
|
||||
taggedAddrs = make(map[string]api.ServiceAddress)
|
||||
for k, v := range c.flagTaggedAddresses {
|
||||
addr, err := api.ParseServiceAddr(v)
|
||||
if err != nil {
|
||||
c.UI.Error(fmt.Sprintf("Invalid Tagged Address: %v", err))
|
||||
return 1
|
||||
}
|
||||
taggedAddrs[k] = addr
|
||||
}
|
||||
}
|
||||
|
||||
svcs := []*api.AgentServiceRegistration{&api.AgentServiceRegistration{
|
||||
ID: c.flagId,
|
||||
Name: c.flagName,
|
||||
Address: c.flagAddress,
|
||||
Port: c.flagPort,
|
||||
Tags: c.flagTags,
|
||||
Meta: c.flagMeta,
|
||||
ID: c.flagId,
|
||||
Name: c.flagName,
|
||||
Address: c.flagAddress,
|
||||
Port: c.flagPort,
|
||||
Tags: c.flagTags,
|
||||
Meta: c.flagMeta,
|
||||
TaggedAddresses: taggedAddrs,
|
||||
}}
|
||||
|
||||
// Check for arg validation
|
||||
|
|
|
@ -118,6 +118,41 @@ func TestCommand_Flags(t *testing.T) {
|
|||
require.NotNil(svc)
|
||||
}
|
||||
|
||||
func TestCommand_Flags_TaggedAddresses(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
require := require.New(t)
|
||||
a := agent.NewTestAgent(t, t.Name(), ``)
|
||||
defer a.Shutdown()
|
||||
client := a.Client()
|
||||
|
||||
ui := cli.NewMockUi()
|
||||
c := New(ui)
|
||||
|
||||
args := []string{
|
||||
"-http-addr=" + a.HTTPAddr(),
|
||||
"-name", "web",
|
||||
"-tagged-address", "lan=127.0.0.1:1234",
|
||||
"-tagged-address", "v6=[2001:db8::12]:1234",
|
||||
}
|
||||
|
||||
require.Equal(0, c.Run(args), ui.ErrorWriter.String())
|
||||
|
||||
svcs, err := client.Agent().Services()
|
||||
require.NoError(err)
|
||||
require.Len(svcs, 1)
|
||||
|
||||
svc := svcs["web"]
|
||||
require.NotNil(svc)
|
||||
require.Len(svc.TaggedAddresses, 2)
|
||||
require.Contains(svc.TaggedAddresses, "lan")
|
||||
require.Contains(svc.TaggedAddresses, "v6")
|
||||
require.Equal(svc.TaggedAddresses["lan"].Address, "127.0.0.1")
|
||||
require.Equal(svc.TaggedAddresses["lan"].Port, 1234)
|
||||
require.Equal(svc.TaggedAddresses["v6"].Address, "2001:db8::12")
|
||||
require.Equal(svc.TaggedAddresses["v6"].Port, 1234)
|
||||
}
|
||||
|
||||
func testFile(t *testing.T, suffix string) *os.File {
|
||||
f := testutil.TempFile(t, "register-test-file")
|
||||
if err := f.Close(); err != nil {
|
||||
|
|
|
@ -58,6 +58,16 @@ $ curl \
|
|||
"ID": "redis",
|
||||
"Service": "redis",
|
||||
"Tags": [],
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.53",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": {
|
||||
"redis_version": "4.0"
|
||||
},
|
||||
|
@ -99,6 +109,9 @@ following selectors and filter operations being supported:
|
|||
| `Proxy.Upstreams.LocalBindAddress` | Equal, Not Equal |
|
||||
| `Proxy.Upstreams.LocalBindPort` | Equal, Not Equal |
|
||||
| `Service` | Equal, Not Equal |
|
||||
| `TaggedAddresses` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `TaggedAddresses.<any>.Address` | Equal, Not Equal |
|
||||
| `TaggedAddresses.<any>.Port` | Equal, Not Equal |
|
||||
| `Tags` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `Weights.Passing` | Equal, Not Equal |
|
||||
| `Weights.Warning` | Equal, Not Equal |
|
||||
|
@ -157,6 +170,16 @@ $ curl \
|
|||
"Meta": null,
|
||||
"Port": 18080,
|
||||
"Address": "",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.53",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Weights": {
|
||||
"Passing": 1,
|
||||
"Warning": 1
|
||||
|
@ -270,6 +293,16 @@ curl localhost:8500/v1/agent/health/service/name/web
|
|||
"rails"
|
||||
],
|
||||
"Address": "",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.53",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": null,
|
||||
"Port": 80,
|
||||
"EnableTagOverride": false,
|
||||
|
@ -290,6 +323,16 @@ curl localhost:8500/v1/agent/health/service/name/web
|
|||
"rails"
|
||||
],
|
||||
"Address": "",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.53",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": null,
|
||||
"Port": 80,
|
||||
"EnableTagOverride": false,
|
||||
|
@ -332,6 +375,16 @@ curl localhost:8500/v1/agent/health/service/id/web2
|
|||
"rails"
|
||||
],
|
||||
"Address": "",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.53",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": null,
|
||||
"Port": 80,
|
||||
"EnableTagOverride": false,
|
||||
|
@ -370,6 +423,16 @@ curl localhost:8500/v1/agent/health/service/id/web1
|
|||
"rails"
|
||||
],
|
||||
"Address": "",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.53",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": null,
|
||||
"Port": 80,
|
||||
"EnableTagOverride": false,
|
||||
|
@ -443,6 +506,10 @@ service definition keys for compatibility with the config file format.
|
|||
provided, the agent's address is used as the address for the service during
|
||||
DNS queries.
|
||||
|
||||
- `TaggedAddresses` `(map<string|object>: nil)` - Specifies a map of explicit LAN
|
||||
and WAN addresses for the service instance. Both the address and port can be
|
||||
specified within the map values.
|
||||
|
||||
- `Meta` `(map<string|string>: nil)` - Specifies arbitrary KV metadata
|
||||
linked to the service instance.
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ The table below shows this endpoint's support for
|
|||
Only one service with a given `ID` may be present per node. The service
|
||||
`Tags`, `Address`, `Meta`, and `Port` fields are all optional. For more
|
||||
information about these fields and the implications of setting them,
|
||||
see the [Service - Agent API](https://www.consul.io/api/agent/service.html) page
|
||||
see the [Service - Agent API](/api/agent/service.html) page
|
||||
as registering services differs between using this or the Services Agent endpoint.
|
||||
|
||||
- `Check` `(Check: nil)` - Specifies to register a check. The register API
|
||||
|
@ -112,6 +112,16 @@ and vice versa. A catalog entry can have either, neither, or both.
|
|||
"v1"
|
||||
],
|
||||
"Address": "127.0.0.1",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "127.0.0.1",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.1",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": {
|
||||
"redis_version": "4.0"
|
||||
},
|
||||
|
@ -475,6 +485,16 @@ $ curl \
|
|||
"ServiceMeta": {
|
||||
"foobar_meta_value": "baz"
|
||||
},
|
||||
"ServiceTaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "172.17.0.3",
|
||||
"port": 5000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.1",
|
||||
"port": 512
|
||||
}
|
||||
},
|
||||
"ServiceTags": [
|
||||
"tacos"
|
||||
],
|
||||
|
@ -529,6 +549,9 @@ $ curl \
|
|||
|
||||
- `ServiceTags` is a list of tags for the service
|
||||
|
||||
- `ServiceTaggedAddresses` is the map of explicit LAN and WAN addresses for the
|
||||
service instance. This includes both the address as well as the port.
|
||||
|
||||
- `ServiceKind` is the kind of service, usually "". See the Agent
|
||||
registration API for more information.
|
||||
|
||||
|
@ -576,6 +599,9 @@ following selectors and filter operations being supported:
|
|||
| `ServiceProxy.Upstreams.DestinationType` | Equal, Not Equal |
|
||||
| `ServiceProxy.Upstreams.LocalBindAddress` | Equal, Not Equal |
|
||||
| `ServiceProxy.Upstreams.LocalBindPort` | Equal, Not Equal |
|
||||
| `ServiceTaggedAddresses` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `ServiceTaggedAddresses.<any>.Address` | Equal, Not Equal |
|
||||
| `ServiceTaggedAddresses.<any>.Port` | Equal, Not Equal |
|
||||
| `ServiceTags` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `ServiceWeights.Passing` | Equal, Not Equal |
|
||||
| `ServiceWeights.Warning` | Equal, Not Equal |
|
||||
|
@ -662,6 +688,16 @@ $ curl \
|
|||
"redis": {
|
||||
"ID": "redis",
|
||||
"Service": "redis",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "10.1.10.12",
|
||||
"port": 8000,
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.1.2",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Tags": [
|
||||
"v1"
|
||||
],
|
||||
|
@ -701,6 +737,9 @@ top level Node object. The following selectors and filter operations are support
|
|||
| `Proxy.Upstreams.LocalBindAddress` | Equal, Not Equal |
|
||||
| `Proxy.Upstreams.LocalBindPort` | Equal, Not Equal |
|
||||
| `Service` | Equal, Not Equal |
|
||||
| `TaggedAddresses` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `TaggedAddresses.<any>.Address` | Equal, Not Equal |
|
||||
| `TaggedAddresses.<any>.Port` | Equal, Not Equal |
|
||||
| `Tags` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `Weights.Passing` | Equal, Not Equal |
|
||||
| `Weights.Warning` | Equal, Not Equal |
|
||||
|
|
|
@ -265,6 +265,16 @@ $ curl \
|
|||
"Service": "redis",
|
||||
"Tags": ["primary"],
|
||||
"Address": "10.1.10.12",
|
||||
"TaggedAddresses": {
|
||||
"lan": {
|
||||
"address": "10.1.10.12",
|
||||
"port": 8000
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.1.2",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"Meta": {
|
||||
"redis_version": "4.0"
|
||||
},
|
||||
|
@ -347,6 +357,9 @@ following selectors and filter operations being supported:
|
|||
| `Service.Proxy.Upstreams.LocalBindAddress` | Equal, Not Equal |
|
||||
| `Service.Proxy.Upstreams.LocalBindPort` | Equal, Not Equal |
|
||||
| `Service.Service` | Equal, Not Equal |
|
||||
| `Service.TaggedAddresses` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `Service.TaggedAddresses.<any>.Address` | Equal, Not Equal |
|
||||
| `Service.TaggedAddresses.<any>.Port` | Equal, Not Equal |
|
||||
| `Service.Tags` | In, Not In, Is Empty, Is Not Empty |
|
||||
| `Service.Weights.Passing` | Equal, Not Equal |
|
||||
| `Service.Weights.Warning` | Equal, Not Equal |
|
||||
|
|
|
@ -37,6 +37,16 @@ example shows all possible fields, but note that only a few are required.
|
|||
"meta": {
|
||||
"meta": "for my service"
|
||||
},
|
||||
"tagged_addresses": {
|
||||
"lan": {
|
||||
"address": "192.168.0.55",
|
||||
"port": 8000,
|
||||
},
|
||||
"wan": {
|
||||
"address": "198.18.0.23",
|
||||
"port": 80
|
||||
}
|
||||
},
|
||||
"port": 8000,
|
||||
"enable_tag_override": false,
|
||||
"checks": [
|
||||
|
@ -280,7 +290,7 @@ For historical reasons Consul's API uses `CamelCased` parameter names in
|
|||
responses, however it's configuration file uses `snake_case` for both HCL and
|
||||
JSON representations. For this reason the registration _HTTP APIs_ accept both
|
||||
name styles for service definition parameters although APIs will return the
|
||||
listings using `CamelCase`.
|
||||
listings using `CamelCase`.
|
||||
|
||||
Note though that **all config file formats require
|
||||
`snake_case` fields**. We always document service definition examples using
|
||||
|
|
Loading…
Reference in New Issue