From cf1aba56a844fc3a32004c7fd17194243eec8d7e Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 26 Mar 2016 12:49:49 -0700 Subject: [PATCH 1/5] Interpolating service tags --- client/driver/executor/executor.go | 10 ++++++++++ client/driver/executor/executor_test.go | 16 ++++++++++++++++ helper/args/args.go | 4 ++-- nomad/mock/mock.go | 1 + 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index 58d20a813..60fd6f40b 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -388,6 +388,9 @@ func (e *UniversalExecutor) SyncServices(ctx *ConsulContext) error { cs.SetDelegatedChecks(e.createCheckMap(), e.createCheck) e.consulService = cs } + if e.ctx != nil { + e.interpolateServices(e.ctx.Task) + } err := e.consulService.SyncTask(e.ctx.Task) go e.consulService.PeriodicSync() return err @@ -541,3 +544,10 @@ func (e *UniversalExecutor) createCheck(check *structs.ServiceCheck, checkID str } return nil, fmt.Errorf("couldn't create check for %v", check.Name) } + +func (e *UniversalExecutor) interpolateServices(task *structs.Task) { + e.ctx.TaskEnv.Build() + for _, service := range task.Services { + service.Tags = e.ctx.TaskEnv.ParseAndReplace(service.Tags) + } +} diff --git a/client/driver/executor/executor_test.go b/client/driver/executor/executor_test.go index f60f97807..0dc91e17c 100644 --- a/client/driver/executor/executor_test.go +++ b/client/driver/executor/executor_test.go @@ -5,6 +5,7 @@ import ( "log" "os" "path/filepath" + "reflect" "strings" "testing" "time" @@ -267,3 +268,18 @@ func TestExecutor_MakeExecutable(t *testing.T) { t.Fatalf("expected permissions %v; got %v", err) } } + +func TestExecutorInterpolateServices(t *testing.T) { + task := mock.Job().TaskGroups[0].Tasks[0] + // Make a fake exececutor + ctx := testExecutorContext(t) + defer ctx.AllocDir.Destroy() + executor := NewExecutor(log.New(os.Stdout, "", log.LstdFlags)) + + executor.(*UniversalExecutor).ctx = ctx + executor.(*UniversalExecutor).interpolateServices(task) + expected := []string{"pci:true", "datacenter:dc1"} + if !reflect.DeepEqual(task.Services[0].Tags, expected) { + t.Fatalf("expected: %v, actual: %v", expected, task.Services[0].Tags) + } +} diff --git a/helper/args/args.go b/helper/args/args.go index 32836f9ea..e97ec5f58 100644 --- a/helper/args/args.go +++ b/helper/args/args.go @@ -9,10 +9,10 @@ var ( // ReplaceEnv takes an arg and replaces all occurences of environment variables. // If the variable is found in the passed map it is replaced, otherwise the // original string is returned. -func ReplaceEnv(arg string, environents ...map[string]string) string { +func ReplaceEnv(arg string, environments ...map[string]string) string { return envRe.ReplaceAllStringFunc(arg, func(arg string) string { stripped := arg[2 : len(arg)-1] - for _, env := range environents { + for _, env := range environments { if value, ok := env[stripped]; ok { return value } diff --git a/nomad/mock/mock.go b/nomad/mock/mock.go index eee97a8d4..432917c80 100644 --- a/nomad/mock/mock.go +++ b/nomad/mock/mock.go @@ -96,6 +96,7 @@ func Job() *structs.Job { { Name: "${TASK}-frontend", PortLabel: "http", + Tags: []string{"pci:${meta.pci-dss}", "datacenter:${node.datacenter}"}, }, { Name: "${TASK}-admin", From 3e7fc261626488482838ca2cdd2cf90b50642ecf Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 26 Mar 2016 12:51:53 -0700 Subject: [PATCH 2/5] Added docs to interpolateServices method --- client/driver/executor/executor.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index 60fd6f40b..e9a45e2d5 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -545,6 +545,8 @@ func (e *UniversalExecutor) createCheck(check *structs.ServiceCheck, checkID str return nil, fmt.Errorf("couldn't create check for %v", check.Name) } +// interpolateServices interpolates tags in a service with values from the +// task's environment. func (e *UniversalExecutor) interpolateServices(task *structs.Task) { e.ctx.TaskEnv.Build() for _, service := range task.Services { From 99a9b1dec8659c254be7e9ea1aa594498e8006be Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Sat, 26 Mar 2016 14:45:04 -0700 Subject: [PATCH 3/5] Updated the docs --- website/source/docs/jobspec/servicediscovery.html.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/website/source/docs/jobspec/servicediscovery.html.md b/website/source/docs/jobspec/servicediscovery.html.md index 23680ad7e..e7468ace8 100644 --- a/website/source/docs/jobspec/servicediscovery.html.md +++ b/website/source/docs/jobspec/servicediscovery.html.md @@ -110,7 +110,8 @@ group "database" { limited to alphanumeric and hyphen characters (i.e. `[a-z0-9\-]`), and be less than 64 characters in length. -* `tags`: A list of tags associated with this Service. +* `tags`: A list of tags associated with this Service. String interpolation is + supported in tags. * `port`: The port indicates the port associated with the service. Users are required to specify a valid port label here which they have defined in the From 8340912a28c23afda18abb2943bc0c012d450b8f Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 28 Mar 2016 12:25:16 -0700 Subject: [PATCH 4/5] Added support for interpolation in check cmd and args --- client/driver/executor/executor.go | 8 +++++++- client/driver/executor/executor_test.go | 16 +++++++++++++--- nomad/mock/mock.go | 14 +++++++++++++- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index e9a45e2d5..2136eef49 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -545,11 +545,17 @@ func (e *UniversalExecutor) createCheck(check *structs.ServiceCheck, checkID str return nil, fmt.Errorf("couldn't create check for %v", check.Name) } -// interpolateServices interpolates tags in a service with values from the +// interpolateServices interpolates tags in a service and checks with values from the // task's environment. func (e *UniversalExecutor) interpolateServices(task *structs.Task) { e.ctx.TaskEnv.Build() for _, service := range task.Services { + for _, check := range service.Checks { + if check.Type == structs.ServiceCheckScript { + check.Cmd = e.ctx.TaskEnv.ReplaceEnv(check.Cmd) + check.Args = e.ctx.TaskEnv.ParseAndReplace(check.Args) + } + } service.Tags = e.ctx.TaskEnv.ParseAndReplace(service.Tags) } } diff --git a/client/driver/executor/executor_test.go b/client/driver/executor/executor_test.go index 0dc91e17c..f5322d0b3 100644 --- a/client/driver/executor/executor_test.go +++ b/client/driver/executor/executor_test.go @@ -278,8 +278,18 @@ func TestExecutorInterpolateServices(t *testing.T) { executor.(*UniversalExecutor).ctx = ctx executor.(*UniversalExecutor).interpolateServices(task) - expected := []string{"pci:true", "datacenter:dc1"} - if !reflect.DeepEqual(task.Services[0].Tags, expected) { - t.Fatalf("expected: %v, actual: %v", expected, task.Services[0].Tags) + expectedTags := []string{"pci:true", "datacenter:dc1"} + if !reflect.DeepEqual(task.Services[0].Tags, expectedTags) { + t.Fatalf("expected: %v, actual: %v", expectedTags, task.Services[0].Tags) + } + + expectedCheckCmd := "/usr/local/check-table-mysql" + expectedCheckArgs := []string{"5.6"} + if !reflect.DeepEqual(task.Services[0].Checks[0].Cmd, expectedCheckCmd) { + t.Fatalf("expected: %v, actual: %v", expectedCheckCmd, task.Services[0].Checks[0].Cmd) + } + + if !reflect.DeepEqual(task.Services[0].Checks[0].Args, expectedCheckArgs) { + t.Fatalf("expected: %v, actual: %v", expectedCheckArgs, task.Services[0].Checks[0].Args) } } diff --git a/nomad/mock/mock.go b/nomad/mock/mock.go index 432917c80..24ea87f20 100644 --- a/nomad/mock/mock.go +++ b/nomad/mock/mock.go @@ -47,7 +47,9 @@ func Node() *structs.Node { "consul": "foobar.dc1", }, Meta: map[string]string{ - "pci-dss": "true", + "pci-dss": "true", + "database": "mysql", + "version": "5.6", }, NodeClass: "linux-medium-pci", Status: structs.NodeStatusReady, @@ -97,6 +99,16 @@ func Job() *structs.Job { Name: "${TASK}-frontend", PortLabel: "http", Tags: []string{"pci:${meta.pci-dss}", "datacenter:${node.datacenter}"}, + Checks: []*structs.ServiceCheck{ + { + Name: "check-table", + Type: structs.ServiceCheckScript, + Cmd: "/usr/local/check-table-${meta.database}", + Args: []string{"${meta.version}"}, + Interval: 30 * time.Second, + Timeout: 5 * time.Second, + }, + }, }, { Name: "${TASK}-admin", From 87399078f0d4f93172db5ad1bbcd943340a56ab7 Mon Sep 17 00:00:00 2001 From: Diptanu Choudhury Date: Mon, 28 Mar 2016 14:36:51 -0700 Subject: [PATCH 5/5] Supporting interpolation in service name, check name/protocol/path --- client/driver/executor/executor.go | 6 +++++- client/driver/executor/executor_test.go | 4 ++-- nomad/mock/mock.go | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/client/driver/executor/executor.go b/client/driver/executor/executor.go index 2136eef49..82dc9c617 100644 --- a/client/driver/executor/executor.go +++ b/client/driver/executor/executor.go @@ -552,10 +552,14 @@ func (e *UniversalExecutor) interpolateServices(task *structs.Task) { for _, service := range task.Services { for _, check := range service.Checks { if check.Type == structs.ServiceCheckScript { - check.Cmd = e.ctx.TaskEnv.ReplaceEnv(check.Cmd) + check.Name = e.ctx.TaskEnv.ReplaceEnv(check.Name) + check.Command = e.ctx.TaskEnv.ReplaceEnv(check.Command) check.Args = e.ctx.TaskEnv.ParseAndReplace(check.Args) + check.Path = e.ctx.TaskEnv.ReplaceEnv(check.Path) + check.Protocol = e.ctx.TaskEnv.ReplaceEnv(check.Protocol) } } + service.Name = e.ctx.TaskEnv.ReplaceEnv(service.Name) service.Tags = e.ctx.TaskEnv.ParseAndReplace(service.Tags) } } diff --git a/client/driver/executor/executor_test.go b/client/driver/executor/executor_test.go index f5322d0b3..c80fa77f6 100644 --- a/client/driver/executor/executor_test.go +++ b/client/driver/executor/executor_test.go @@ -285,8 +285,8 @@ func TestExecutorInterpolateServices(t *testing.T) { expectedCheckCmd := "/usr/local/check-table-mysql" expectedCheckArgs := []string{"5.6"} - if !reflect.DeepEqual(task.Services[0].Checks[0].Cmd, expectedCheckCmd) { - t.Fatalf("expected: %v, actual: %v", expectedCheckCmd, task.Services[0].Checks[0].Cmd) + if !reflect.DeepEqual(task.Services[0].Checks[0].Command, expectedCheckCmd) { + t.Fatalf("expected: %v, actual: %v", expectedCheckCmd, task.Services[0].Checks[0].Command) } if !reflect.DeepEqual(task.Services[0].Checks[0].Args, expectedCheckArgs) { diff --git a/nomad/mock/mock.go b/nomad/mock/mock.go index 24ea87f20..3eed7dd75 100644 --- a/nomad/mock/mock.go +++ b/nomad/mock/mock.go @@ -103,7 +103,7 @@ func Job() *structs.Job { { Name: "check-table", Type: structs.ServiceCheckScript, - Cmd: "/usr/local/check-table-${meta.database}", + Command: "/usr/local/check-table-${meta.database}", Args: []string{"${meta.version}"}, Interval: 30 * time.Second, Timeout: 5 * time.Second,