diff --git a/config/config_test.go b/config/config_test.go index 2372c3df..f7f99472 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -32,6 +32,7 @@ func TestValidConfigJobs(t *testing.T) { assert.Equal(job0.Port, 8080, "config for job0.Port") assert.Equal(job0.Exec, "/bin/serviceA", "config for job0.Exec") assert.Equal(job0.Tags, []string{"tag1", "tag2"}, "config for job0.Tags") + assert.Equal(job0.Meta, map[string]string{"keyA": "A"}, "config for job0.Meta") assert.Equal(job0.Restarts, nil, "config for job1.Restarts") job1 := cfg.Jobs[1] diff --git a/config/testdata/test.json5 b/config/testdata/test.json5 index 70c912ae..b2b1bfce 100644 --- a/config/testdata/test.json5 +++ b/config/testdata/test.json5 @@ -18,7 +18,10 @@ interval: 19, ttl: 30, }, - tags: ["tag1","tag2"] + tags: ["tag1","tag2"], + meta: { + keyA: "A", + } }, { name: "serviceB", diff --git a/discovery/consul_test.go b/discovery/consul_test.go index bd6ea898..ba8289db 100644 --- a/discovery/consul_test.go +++ b/discovery/consul_test.go @@ -84,6 +84,7 @@ func TestWithConsul(t *testing.T) { t.Run("TestConsulReregister", testConsulReregister(testServer)) t.Run("TestConsulCheckForChanges", testConsulCheckForChanges(testServer)) t.Run("TestConsulEnableTagOverride", testConsulEnableTagOverride(testServer)) + t.Run("testConsulTagsMeta", testConsulTagsMeta(testServer)) } func testConsulTTLPass(testServer *TestServer) func(*testing.T) { @@ -146,6 +147,39 @@ func testConsulReregister(testServer *TestServer) func(*testing.T) { } } +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + +func testConsulTagsMeta(testServer *TestServer) func(*testing.T) { + return func(t *testing.T) { + consul, _ := NewConsul(testServer.HTTPAddr) + name := "TestConsulReregister" + service := generateServiceDefinition(name, consul) + id := service.ID + + service.SendHeartbeat() // force registration and 1st heartbeat + services, _ := consul.Agent().Services() + svc := services[id] + if !contains(svc.Tags, "a") || !contains(svc.Tags, "b") { + t.Fatalf("first tag must containt a & b but is %s", svc.Tags) + } + if svc.Meta["keyA"] != "A" { + t.Fatalf("first meta must containt keyA:A but is %s", svc.Meta["keyA"]) + } + if svc.Meta["keyB"] != "B" { + t.Fatalf("first meta must containt keyB:B but is %s", svc.Meta["keyB"]) + } + + } +} + func testConsulCheckForChanges(testServer *TestServer) func(*testing.T) { return func(t *testing.T) { backend := "TestConsulCheckForChanges" @@ -211,5 +245,10 @@ func generateServiceDefinition(serviceName string, consul *Consul) *ServiceDefin TTL: 5, Port: 9000, Consul: consul, + Tags: []string{"a", "b"}, + Meta: map[string]string{ + "keyA": "A", + "keyB": "B", + }, } } diff --git a/discovery/service.go b/discovery/service.go index b99211bd..8ce302f8 100644 --- a/discovery/service.go +++ b/discovery/service.go @@ -15,6 +15,7 @@ type ServiceDefinition struct { Port int TTL int Tags []string + Meta map[string]string InitialStatus string IPAddress string EnableTagOverride bool @@ -93,6 +94,7 @@ func (service *ServiceDefinition) registerService(status string) error { ID: service.ID, Name: service.Name, Tags: service.Tags, + Meta: service.Meta, Port: service.Port, Address: service.IPAddress, EnableTagOverride: service.EnableTagOverride, diff --git a/docs/30-configuration/32-configuration-file.md b/docs/30-configuration/32-configuration-file.md index 2c3b21ab..75bbfc99 100644 --- a/docs/30-configuration/32-configuration-file.md +++ b/docs/30-configuration/32-configuration-file.md @@ -60,6 +60,10 @@ The following is a completed example of the JSON5 file configuration schema, wit "app", "prod" ], + meta: { + keyA: "A", + keyB: "B", + }, interfaces: [ "eth0", "eth1[1]", diff --git a/docs/30-configuration/34-jobs.md b/docs/30-configuration/34-jobs.md index 14e708bd..3063915e 100644 --- a/docs/30-configuration/34-jobs.md +++ b/docs/30-configuration/34-jobs.md @@ -81,6 +81,10 @@ jobs: [ "app", "prod" ], + meta: { + keyA: "A", + keyB: "B", + }, interfaces: [ "eth0", "eth1[1]", @@ -233,6 +237,10 @@ The `initial_status` field is optional and specifies which status to immediately The `tags` field is an optional array of tags to be used when the job is registered as a service in Consul. Other containers can use these tags in `watches` to filter a service by tag. +##### `meta` + +The `meta` field is an optional map key/value to be used when the job is registered as a service in Consul. Key names must be valid JSON5/Ecmascript identifierNames or be quoted and follow consul limitation , practical this means only [a-zA-Z0-9_-] can be used in key names and key names with '-' must be quoted + ##### `interfaces` The `interfaces` field is an optional single or array of interface specifications. If given, the IP of the service will be obtained from the first interface specification that matches. (Default value is `["eth0:inet"]`). The value that ContainerPilot uses for the IP address of the interface will be set as an environment variable with the name `CONTAINERPILOT_{JOB}_IP`. See the [environment variables](./32-configuration-file.md#environment-variables) section. diff --git a/jobs/config.go b/jobs/config.go index a62e1ec0..9386826b 100644 --- a/jobs/config.go +++ b/jobs/config.go @@ -23,11 +23,12 @@ type Config struct { Exec interface{} `mapstructure:"exec"` // service discovery - Port int `mapstructure:"port"` - InitialStatus string `mapstructure:"initial_status"` - Interfaces interface{} `mapstructure:"interfaces"` - Tags []string `mapstructure:"tags"` - ConsulExtras *ConsulExtras `mapstructure:"consul"` + Port int `mapstructure:"port"` + InitialStatus string `mapstructure:"initial_status"` + Interfaces interface{} `mapstructure:"interfaces"` + Tags []string `mapstructure:"tags"` + Meta map[string]string `mapstructure:"meta"` + ConsulExtras *ConsulExtras `mapstructure:"consul"` serviceDefinition *discovery.ServiceDefinition // health checking @@ -430,6 +431,7 @@ func (cfg *Config) addDiscoveryConfig(disc discovery.Backend) error { Port: cfg.Port, TTL: cfg.ttl, Tags: cfg.Tags, + Meta: cfg.Meta, InitialStatus: cfg.InitialStatus, IPAddress: ipAddress, DeregisterCriticalServiceAfter: deregAfter,