mirror of
https://github.com/kemko/nomad.git
synced 2026-01-05 01:45:44 +03:00
consul: add support for canary meta
This commit is contained in:
@@ -48,6 +48,7 @@ IMPROVEMENTS:
|
||||
* api: Added JSON representation of rules to policy endpoint response [[GH-6017](https://github.com/hashicorp/nomad/pull/6017)]
|
||||
* api: Update policy endpoint to permit anonymous access [[GH-6021](https://github.com/hashicorp/nomad/issues/6021)]
|
||||
* build: Updated to Go 1.12.13 [[GH-6606](https://github.com/hashicorp/nomad/issues/6606)]
|
||||
* consul: Add support for service `canary_meta`
|
||||
* cli: Show full ID in node and alloc individual status views [[GH-6425](https://github.com/hashicorp/nomad/issues/6425)]
|
||||
* client: Enable setting tags on Consul Connect sidecar service [[GH-6448](https://github.com/hashicorp/nomad/issues/6448)]
|
||||
* client: Added support for downloading artifacts from Google Cloud Storage [[GH-6692](https://github.com/hashicorp/nomad/pull/6692)]
|
||||
|
||||
@@ -107,6 +107,7 @@ type Service struct {
|
||||
CheckRestart *CheckRestart `mapstructure:"check_restart"`
|
||||
Connect *ConsulConnect
|
||||
Meta map[string]string
|
||||
CanaryMeta map[string]string
|
||||
}
|
||||
|
||||
// Canonicalize the Service by ensuring its name and address mode are set. Task
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"sync"
|
||||
|
||||
log "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/nomad/helper"
|
||||
|
||||
"github.com/hashicorp/consul/api"
|
||||
)
|
||||
@@ -111,6 +112,7 @@ func (c *MockAgent) Services() (map[string]*api.AgentService, error) {
|
||||
ID: v.ID,
|
||||
Service: v.Name,
|
||||
Tags: make([]string, len(v.Tags)),
|
||||
Meta: helper.CopyMapStringString(v.Meta),
|
||||
Port: v.Port,
|
||||
Address: v.Address,
|
||||
EnableTagOverride: v.EnableTagOverride,
|
||||
|
||||
@@ -99,7 +99,8 @@ func agentServiceUpdateRequired(reg *api.AgentServiceRegistration, svc *api.Agen
|
||||
reg.Port == svc.Port &&
|
||||
reg.Address == svc.Address &&
|
||||
reg.Name == svc.Service &&
|
||||
reflect.DeepEqual(reg.Tags, svc.Tags))
|
||||
reflect.DeepEqual(reg.Tags, svc.Tags) &&
|
||||
reflect.DeepEqual(reg.Meta, svc.Meta))
|
||||
}
|
||||
|
||||
// operations are submitted to the main loop via commit() for synchronizing
|
||||
@@ -713,9 +714,17 @@ func (c *ServiceClient) serviceRegs(ops *operations, service *structs.Service, w
|
||||
return nil, fmt.Errorf("invalid Consul Connect configuration for service %q: %v", service.Name, err)
|
||||
}
|
||||
|
||||
meta := make(map[string]string, len(service.Meta))
|
||||
for k, v := range service.Meta {
|
||||
meta[k] = v
|
||||
var meta map[string]string
|
||||
if task.Canary && len(service.CanaryMeta) > 0 {
|
||||
meta = make(map[string]string, len(service.CanaryMeta)+1)
|
||||
for k, v := range service.CanaryMeta {
|
||||
meta[k] = v
|
||||
}
|
||||
} else {
|
||||
meta = make(map[string]string, len(service.Meta)+1)
|
||||
for k, v := range service.Meta {
|
||||
meta[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// This enables the consul UI to show that Nomad registered this service
|
||||
|
||||
@@ -35,6 +35,7 @@ func testWorkload() *WorkloadServices {
|
||||
Name: "taskname-service",
|
||||
PortLabel: "x",
|
||||
Tags: []string{"tag1", "tag2"},
|
||||
Meta: map[string]string{"meta1": "foo"},
|
||||
},
|
||||
},
|
||||
Networks: []*structs.NetworkResource{
|
||||
@@ -1077,6 +1078,73 @@ func TestConsul_CanaryTags_NoTags(t *testing.T) {
|
||||
require.Len(ctx.FakeConsul.services, 0)
|
||||
}
|
||||
|
||||
// TestConsul_CanaryMeta asserts CanaryMeta are used when Canary=true
|
||||
func TestConsul_CanaryMeta(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
ctx := setupFake(t)
|
||||
|
||||
canaryMeta := map[string]string{"meta1": "canary"}
|
||||
canaryMeta["external-source"] = "nomad"
|
||||
ctx.Task.Canary = true
|
||||
ctx.Task.Services[0].CanaryMeta = canaryMeta
|
||||
|
||||
require.NoError(ctx.ServiceClient.RegisterTask(ctx.Task))
|
||||
require.NoError(ctx.syncOnce())
|
||||
require.Len(ctx.FakeConsul.services, 1)
|
||||
for _, service := range ctx.FakeConsul.services {
|
||||
require.Equal(canaryMeta, service.Meta)
|
||||
}
|
||||
|
||||
// Disable canary and assert meta are not the canary meta
|
||||
origTask := ctx.Task.Copy()
|
||||
ctx.Task.Canary = false
|
||||
require.NoError(ctx.ServiceClient.UpdateTask(origTask, ctx.Task))
|
||||
require.NoError(ctx.syncOnce())
|
||||
require.Len(ctx.FakeConsul.services, 1)
|
||||
for _, service := range ctx.FakeConsul.services {
|
||||
require.NotEqual(canaryMeta, service.Meta)
|
||||
}
|
||||
|
||||
ctx.ServiceClient.RemoveTask(ctx.Task)
|
||||
require.NoError(ctx.syncOnce())
|
||||
require.Len(ctx.FakeConsul.services, 0)
|
||||
}
|
||||
|
||||
// TestConsul_CanaryMeta_NoMeta asserts Meta are used when Canary=true and there
|
||||
// are no specified canary meta
|
||||
func TestConsul_CanaryMeta_NoMeta(t *testing.T) {
|
||||
t.Parallel()
|
||||
require := require.New(t)
|
||||
ctx := setupFake(t)
|
||||
|
||||
meta := map[string]string{"meta1": "foo"}
|
||||
meta["external-source"] = "nomad"
|
||||
ctx.Task.Canary = true
|
||||
ctx.Task.Services[0].Meta = meta
|
||||
|
||||
require.NoError(ctx.ServiceClient.RegisterTask(ctx.Task))
|
||||
require.NoError(ctx.syncOnce())
|
||||
require.Len(ctx.FakeConsul.services, 1)
|
||||
for _, service := range ctx.FakeConsul.services {
|
||||
require.Equal(meta, service.Meta)
|
||||
}
|
||||
|
||||
// Disable canary and assert meta dont change
|
||||
origTask := ctx.Task.Copy()
|
||||
ctx.Task.Canary = false
|
||||
require.NoError(ctx.ServiceClient.UpdateTask(origTask, ctx.Task))
|
||||
require.NoError(ctx.syncOnce())
|
||||
require.Len(ctx.FakeConsul.services, 1)
|
||||
for _, service := range ctx.FakeConsul.services {
|
||||
require.Equal(meta, service.Meta)
|
||||
}
|
||||
|
||||
ctx.ServiceClient.RemoveTask(ctx.Task)
|
||||
require.NoError(ctx.syncOnce())
|
||||
require.Len(ctx.FakeConsul.services, 0)
|
||||
}
|
||||
|
||||
// TestConsul_PeriodicSync asserts that Nomad periodically reconciles with
|
||||
// Consul.
|
||||
func TestConsul_PeriodicSync(t *testing.T) {
|
||||
|
||||
@@ -834,6 +834,7 @@ func ApiTaskToStructsTask(apiTask *api.Task, structsTask *structs.Task) {
|
||||
CanaryTags: service.CanaryTags,
|
||||
AddressMode: service.AddressMode,
|
||||
Meta: helper.CopyMapStringString(service.Meta),
|
||||
CanaryMeta: helper.CopyMapStringString(service.CanaryMeta),
|
||||
}
|
||||
|
||||
if l := len(service.Checks); l != 0 {
|
||||
@@ -1012,6 +1013,7 @@ func ApiServicesToStructs(in []*api.Service) []*structs.Service {
|
||||
CanaryTags: s.CanaryTags,
|
||||
AddressMode: s.AddressMode,
|
||||
Meta: helper.CopyMapStringString(s.Meta),
|
||||
CanaryMeta: helper.CopyMapStringString(s.CanaryMeta),
|
||||
}
|
||||
|
||||
if l := len(s.Checks); l != 0 {
|
||||
|
||||
@@ -47,6 +47,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
"check_restart",
|
||||
"connect",
|
||||
"meta",
|
||||
"canary_meta",
|
||||
}
|
||||
if err := helper.CheckHCLKeys(o.Val, valid); err != nil {
|
||||
return nil, err
|
||||
@@ -62,6 +63,7 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
delete(m, "check_restart")
|
||||
delete(m, "connect")
|
||||
delete(m, "meta")
|
||||
delete(m, "canary_meta")
|
||||
|
||||
if err := mapstructure.WeakDecode(m, &service); err != nil {
|
||||
return nil, err
|
||||
@@ -122,6 +124,20 @@ func parseService(o *ast.ObjectItem) (*api.Service, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Parse out canary_meta fields. These are in HCL as a list so we need
|
||||
// to iterate over them and merge them.
|
||||
if metaO := listVal.Filter("canary_meta"); len(metaO.Items) > 0 {
|
||||
for _, o := range metaO.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.CanaryMeta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &service, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -218,7 +218,13 @@ func TestParse(t *testing.T) {
|
||||
{
|
||||
Tags: []string{"foo", "bar"},
|
||||
CanaryTags: []string{"canary", "bam"},
|
||||
PortLabel: "http",
|
||||
Meta: map[string]string{
|
||||
"abc": "123",
|
||||
},
|
||||
CanaryMeta: map[string]string{
|
||||
"canary": "boom",
|
||||
},
|
||||
PortLabel: "http",
|
||||
Checks: []api.ServiceCheck{
|
||||
{
|
||||
Name: "check-name",
|
||||
|
||||
@@ -169,6 +169,14 @@ job "binstore-storagelocker" {
|
||||
}
|
||||
|
||||
service {
|
||||
meta {
|
||||
abc = "123"
|
||||
}
|
||||
|
||||
canary_meta {
|
||||
canary = "boom"
|
||||
}
|
||||
|
||||
tags = ["foo", "bar"]
|
||||
canary_tags = ["canary", "bam"]
|
||||
port = "http"
|
||||
|
||||
@@ -331,6 +331,7 @@ type Service struct {
|
||||
Checks []*ServiceCheck // List of checks associated with the service
|
||||
Connect *ConsulConnect // Consul Connect configuration
|
||||
Meta map[string]string // Consul service meta
|
||||
CanaryMeta map[string]string // Consul service meta when it is a canary
|
||||
}
|
||||
|
||||
// Copy the stanza recursively. Returns nil if nil.
|
||||
@@ -354,6 +355,7 @@ func (s *Service) Copy() *Service {
|
||||
ns.Connect = s.Connect.Copy()
|
||||
|
||||
ns.Meta = helper.CopyMapStringString(s.Meta)
|
||||
ns.CanaryMeta = helper.CopyMapStringString(s.CanaryMeta)
|
||||
|
||||
return ns
|
||||
}
|
||||
@@ -466,6 +468,9 @@ func (s *Service) Hash(allocID, taskName string, canary bool) string {
|
||||
if len(s.Meta) > 0 {
|
||||
fmt.Fprintf(h, "%v", s.Meta)
|
||||
}
|
||||
if len(s.CanaryMeta) > 0 {
|
||||
fmt.Fprintf(h, "%v", s.CanaryMeta)
|
||||
}
|
||||
|
||||
// Vary ID on whether or not CanaryTags will be used
|
||||
if canary {
|
||||
@@ -526,6 +531,10 @@ OUTER:
|
||||
return false
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(s.CanaryMeta, o.CanaryMeta) {
|
||||
return false
|
||||
}
|
||||
|
||||
if !helper.CompareSliceSetString(s.Tags, o.Tags) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -128,7 +128,7 @@ Connect][connect] integration.
|
||||
this service when the service is part of an allocation that is currently a
|
||||
canary. Once the canary is promoted, the registered tags will be updated to
|
||||
those specified in the `tags` parameter. If this is not supplied, the
|
||||
registered tags will be equal to that of the `tags parameter.
|
||||
registered tags will be equal to that of the `tags` parameter.
|
||||
|
||||
- `address_mode` `(string: "auto")` - Specifies what address (host or
|
||||
driver-specific) this service should advertise. This setting is supported in
|
||||
@@ -151,6 +151,13 @@ Connect][connect] integration.
|
||||
- `meta` <code>([Meta][]: nil)</code> - Specifies a key-value map that annotates
|
||||
the Consul service with user-defined metadata.
|
||||
|
||||
- `canary_meta` <code>([Meta][]: nil)</code> - Specifies a key-value map that
|
||||
annotates the Consul service with user-defined metadata when the service is
|
||||
part of an allocation that is currently a canary. Once the canary is
|
||||
promoted, the registered meta will be updated to those specified in the
|
||||
`meta` parameter. If this is not supploed, the registered meta will be set to
|
||||
that of the `meta` parameter.
|
||||
|
||||
### `check` Parameters
|
||||
|
||||
Note that health checks run inside the task. If your task is a Docker container,
|
||||
|
||||
Reference in New Issue
Block a user