diff --git a/api/services.go b/api/services.go index 0f4ea3d2ecaa..07464cb61ecb 100644 --- a/api/services.go +++ b/api/services.go @@ -236,6 +236,7 @@ type Service struct { Connect *ConsulConnect `hcl:"connect,block"` Meta map[string]string `hcl:"meta,block"` CanaryMeta map[string]string `hcl:"canary_meta,block"` + TaggedAddresses map[string]string `hcl:"tagged_addresses,block"` TaskName string `mapstructure:"task" hcl:"task,optional"` OnUpdate string `mapstructure:"on_update" hcl:"on_update,optional"` diff --git a/client/taskenv/services.go b/client/taskenv/services.go index e2a251af417e..18eede7de105 100644 --- a/client/taskenv/services.go +++ b/client/taskenv/services.go @@ -41,6 +41,7 @@ func InterpolateServices(taskEnv *TaskEnv, services []*structs.Service) []*struc service.CanaryTags = taskEnv.ParseAndReplace(service.CanaryTags) service.Meta = interpolateMapStringString(taskEnv, service.Meta) service.CanaryMeta = interpolateMapStringString(taskEnv, service.CanaryMeta) + service.TaggedAddresses = interpolateMapStringString(taskEnv, service.TaggedAddresses) interpolateConnect(taskEnv, service.Connect) interpolated[i] = service diff --git a/command/agent/consul/service_client.go b/command/agent/consul/service_client.go index 15ecdc6c8e5b..21a15db3a886 100644 --- a/command/agent/consul/service_client.go +++ b/command/agent/consul/service_client.go @@ -213,6 +213,8 @@ func different(wanted *api.AgentServiceRegistration, existing *api.AgentService, return true case !reflect.DeepEqual(wanted.Meta, existing.Meta): return true + case !reflect.DeepEqual(wanted.TaggedAddresses, existing.TaggedAddresses): + return true case tagsDifferent(wanted.Tags, existing.Tags): return true case connectSidecarDifferent(wanted, sidecar): @@ -1027,6 +1029,19 @@ func (c *ServiceClient) serviceRegs( } } + var taggedAddresses map[string]api.ServiceAddress + for k, v := range service.TaggedAddresses { + sa, err := parseAddress(v, port) + if err != nil { + c.logger.Warn("failed to parse advertise address", "name", k, "adrress", v) + continue + } + if taggedAddresses == nil { + taggedAddresses = make(map[string]api.ServiceAddress) + } + taggedAddresses[k] = sa + } + // Build the Consul Service registration request serviceReg := &api.AgentServiceRegistration{ Kind: kind, @@ -1038,6 +1053,7 @@ func (c *ServiceClient) serviceRegs( Address: ip, Port: port, Meta: meta, + TaggedAddresses: taggedAddresses, Connect: connect, // will be nil if no Connect stanza Proxy: gateway, // will be nil if no Connect Gateway stanza } @@ -1664,3 +1680,31 @@ func getNomadSidecar(id string, services map[string]*api.AgentService) *api.Agen sidecarID := id + sidecarSuffix return services[sidecarID] } + +func parseAddress(raw string, port int) (api.ServiceAddress, error) { + result := api.ServiceAddress{} + addr, portStr, err := net.SplitHostPort(raw) + // Error message from Go's net/ipsock.go + if err != nil { + if !strings.Contains(err.Error(), "missing port in address") { + return result, fmt.Errorf("error parsing address %q: %v", raw, err) + } + + // Use the whole input as the address if there wasn't a port. + if ip := net.ParseIP(raw); ip == nil { + return result, fmt.Errorf("error parsing address %q: not an IP address", raw) + } + addr = raw + } + + if portStr != "" { + port, err = strconv.Atoi(portStr) + if err != nil { + return result, fmt.Errorf("error parsing port %q: %v", portStr, err) + } + } + + result.Address = addr + result.Port = port + return result, nil +} diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 46739207d486..5598e41a3a40 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -1376,6 +1376,7 @@ func ApiServicesToStructs(in []*api.Service, group bool) []*structs.Service { Address: s.Address, Meta: helper.CopyMapStringString(s.Meta), CanaryMeta: helper.CopyMapStringString(s.CanaryMeta), + TaggedAddresses: helper.CopyMapStringString(s.TaggedAddresses), OnUpdate: s.OnUpdate, Provider: s.Provider, } diff --git a/jobspec/parse_service.go b/jobspec/parse_service.go index 9ca32e352ae6..278942fefead 100644 --- a/jobspec/parse_service.go +++ b/jobspec/parse_service.go @@ -50,6 +50,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) { "task", "meta", "canary_meta", + "tagged_addresses", "on_update", "provider", } @@ -68,6 +69,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) { delete(m, "connect") delete(m, "meta") delete(m, "canary_meta") + delete(m, "tagged_addresses") if err := mapstructure.WeakDecode(m, &service); err != nil { return nil, err @@ -140,6 +142,20 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) { } } + // Parse out tagged_addresses fields. These are in HCL as a list so we need + // to iterate over them and merge them. + if taO := listVal.Filter("tagged_addresses"); len(taO.Items) > 0 { + for _, o := range taO.Elem().Items { + var m map[string]interface{} + if err := hcl.DecodeObject(&m, o.Val); err != nil { + return nil, err + } + if err := mapstructure.WeakDecode(m, &service.TaggedAddresses); err != nil { + return nil, err + } + } + } + return &service, nil } diff --git a/nomad/structs/services.go b/nomad/structs/services.go index c0643e05d39e..a53ed4259339 100644 --- a/nomad/structs/services.go +++ b/nomad/structs/services.go @@ -472,6 +472,8 @@ type Service struct { Meta map[string]string // Consul service meta CanaryMeta map[string]string // Consul service meta when it is a canary + TaggedAddresses map[string]string + // The consul namespace in which this service will be registered. Namespace // at the service.check level is not part of the Nomad API - it must be // set at the job or group level. This field is managed internally so @@ -516,6 +518,7 @@ func (s *Service) Copy() *Service { ns.Meta = helper.CopyMapStringString(s.Meta) ns.CanaryMeta = helper.CopyMapStringString(s.CanaryMeta) + ns.TaggedAddresses = helper.CopyMapStringString(s.TaggedAddresses) return ns } @@ -699,6 +702,7 @@ func (s *Service) Hash(allocID, taskName string, canary bool) string { hashBool(h, s.EnableTagOverride, "ETO") hashMeta(h, s.Meta) hashMeta(h, s.CanaryMeta) + hashMeta(h, s.TaggedAddresses) hashConnect(h, s.Connect) hashString(h, s.OnUpdate) hashString(h, s.Namespace) @@ -824,6 +828,10 @@ OUTER: return false } + if !reflect.DeepEqual(s.TaggedAddresses, o.TaggedAddresses) { + return false + } + if !helper.CompareSliceSetString(s.Tags, o.Tags) { return false }