diff --git a/api/tasks.go b/api/tasks.go index 46ef0cf38..c378c222d 100644 --- a/api/tasks.go +++ b/api/tasks.go @@ -1,8 +1,6 @@ package api import ( - "fmt" - "github.com/hashicorp/nomad/helper/args" "time" ) @@ -44,16 +42,6 @@ type Service struct { Checks []ServiceCheck } -func (s *Service) ExpandName(job string, taskGroup string, task string) { - s.Name = args.ReplaceEnv(s.Name, map[string]string{ - "JOB": job, - "TASKGROUP": taskGroup, - "TASK": task, - "BASE": fmt.Sprintf("%s-%s-%s", job, taskGroup, task), - }, - ) -} - // TaskGroup is the unit of scheduling. type TaskGroup struct { Name string diff --git a/api/tasks_test.go b/api/tasks_test.go index 0baa315ba..75f29996d 100644 --- a/api/tasks_test.go +++ b/api/tasks_test.go @@ -218,37 +218,3 @@ func TestTask_Constrain(t *testing.T) { t.Fatalf("expect: %#v, got: %#v", expect, task.Constraints) } } - -func TestService_Expand_Name(t *testing.T) { - job := "example" - taskGroup := "cache" - task := "redis" - - s := Service{ - Name: "${TASK}-db", - } - - s.ExpandName(job, taskGroup, task) - if s.Name != "redis-db" { - t.Fatalf("Expected name: %v, Actual: %v", "redis-db", s.Name) - } - - s.Name = "db" - s.ExpandName(job, taskGroup, task) - if s.Name != "db" { - t.Fatalf("Expected name: %v, Actual: %v", "redis-db", s.Name) - } - - s.Name = "${JOB}-${TASKGROUP}-${TASK}-db" - s.ExpandName(job, taskGroup, task) - if s.Name != "example-cache-redis-db" { - t.Fatalf("Expected name: %v, Actual: %v", "expample-cache-redis-db", s.Name) - } - - s.Name = "${BASE}-db" - s.ExpandName(job, taskGroup, task) - if s.Name != "example-cache-redis-db" { - t.Fatalf("Expected name: %v, Actual: %v", "expample-cache-redis-db", s.Name) - } - -} diff --git a/nomad/job_endpoint.go b/nomad/job_endpoint.go index e961428e4..384398fd7 100644 --- a/nomad/job_endpoint.go +++ b/nomad/job_endpoint.go @@ -28,6 +28,9 @@ func (j *Job) Register(args *structs.JobRegisterRequest, reply *structs.JobRegis if err := args.Job.Validate(); err != nil { return err } + + args.Job.ExpandAllServiceNames() + if args.Job.Type == structs.JobTypeCore { return fmt.Errorf("job type cannot be core") } diff --git a/nomad/job_endpoint_test.go b/nomad/job_endpoint_test.go index c12e5b463..e38ae5a6a 100644 --- a/nomad/job_endpoint_test.go +++ b/nomad/job_endpoint_test.go @@ -47,6 +47,10 @@ func TestJobEndpoint_Register(t *testing.T) { if out.CreateIndex != resp.JobModifyIndex { t.Fatalf("index mis-match") } + serviceName := out.TaskGroups[0].Tasks[0].Services[0].Name + if serviceName != "web-frontend" { + t.Fatalf("Expected Service Name: %s, Actual: %s", serviceName) + } // Lookup the evaluation eval, err := state.EvalByID(resp.EvalID) diff --git a/nomad/mock/mock.go b/nomad/mock/mock.go index 53f246700..0d2949d80 100644 --- a/nomad/mock/mock.go +++ b/nomad/mock/mock.go @@ -90,6 +90,12 @@ func Job() *structs.Job { Env: map[string]string{ "FOO": "bar", }, + Services: []*structs.Service{ + { + Name: "${TASK}-frontend", + PortLabel: "http", + }, + }, Resources: &structs.Resources{ CPU: 500, MemoryMB: 256, diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 8aaf0e7ef..44a55175c 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -14,6 +14,7 @@ import ( "github.com/hashicorp/go-msgpack/codec" "github.com/hashicorp/go-multierror" "github.com/hashicorp/go-version" + "github.com/hashicorp/nomad/helper/args" ) var ( @@ -774,6 +775,14 @@ type Job struct { ModifyIndex uint64 } +// ExpandAllServiceNames traverses all Task Groups and makes them +// interpolate Job, Task group and Task names in all Service names +func (j *Job) ExpandAllServiceNames() { + for _, tg := range j.TaskGroups { + tg.ExpandAllServiceNames(j.Name) + } +} + // Validate is used to sanity check a job input func (j *Job) Validate() error { var mErr multierror.Error @@ -942,6 +951,14 @@ type TaskGroup struct { Meta map[string]string } +// ExpandAllServiceNames traverses over all Tasks and makes them to interpolate +// values of Job, Task Group and Task names in all Service Names +func (tg *TaskGroup) ExpandAllServiceNames(job string) { + for _, task := range tg.Tasks { + task.ExpandAllServiceNames(job, tg.Name) + } +} + // Validate is used to sanity check a task group func (tg *TaskGroup) Validate() error { var mErr multierror.Error @@ -1025,6 +1042,16 @@ type ServiceCheck struct { Timeout time.Duration // Timeout of the response from the check before consul fails the check } +func (s *Service) ExpandName(job string, taskGroup string, task string) { + s.Name = args.ReplaceEnv(s.Name, map[string]string{ + "JOB": job, + "TASKGROUP": taskGroup, + "TASK": task, + "BASE": fmt.Sprintf("%s-%s-%s", job, taskGroup, task), + }, + ) +} + func (sc *ServiceCheck) Validate() error { t := strings.ToLower(sc.Type) if t != ServiceCheckTCP && t != ServiceCheckHTTP { @@ -1109,6 +1136,14 @@ type Task struct { Meta map[string]string } +// ExpandAllServiceNames interpolates values of Job, Task Group +// and Tasks in all the service Names of a Task +func (t *Task) ExpandAllServiceNames(job string, taskGroup string) { + for _, service := range t.Services { + service.ExpandName(job, taskGroup, t.Name) + } +} + func (t *Task) GoString() string { return fmt.Sprintf("*%#v", *t) } diff --git a/nomad/structs/structs_test.go b/nomad/structs/structs_test.go index a7df0d523..7d1c6a1f0 100644 --- a/nomad/structs/structs_test.go +++ b/nomad/structs/structs_test.go @@ -405,3 +405,82 @@ func TestDistinctCheckId(t *testing.T) { } } + +func TestService_Expand_Name(t *testing.T) { + job := "example" + taskGroup := "cache" + task := "redis" + + s := Service{ + Name: "${TASK}-db", + } + + s.ExpandName(job, taskGroup, task) + if s.Name != "redis-db" { + t.Fatalf("Expected name: %v, Actual: %v", "redis-db", s.Name) + } + + s.Name = "db" + s.ExpandName(job, taskGroup, task) + if s.Name != "db" { + t.Fatalf("Expected name: %v, Actual: %v", "redis-db", s.Name) + } + + s.Name = "${JOB}-${TASKGROUP}-${TASK}-db" + s.ExpandName(job, taskGroup, task) + if s.Name != "example-cache-redis-db" { + t.Fatalf("Expected name: %v, Actual: %v", "expample-cache-redis-db", s.Name) + } + + s.Name = "${BASE}-db" + s.ExpandName(job, taskGroup, task) + if s.Name != "example-cache-redis-db" { + t.Fatalf("Expected name: %v, Actual: %v", "expample-cache-redis-db", s.Name) + } + +} + +func TestJob_ExpandServiceNames(t *testing.T) { + j := &Job{ + Name: "my-job", + TaskGroups: []*TaskGroup{ + &TaskGroup{ + Name: "web", + Tasks: []*Task{ + { + Name: "frontend", + Services: []*Service{ + { + Name: "${BASE}-default", + }, + { + Name: "jmx", + }, + }, + }, + }, + }, + &TaskGroup{ + Name: "admin", + Tasks: []*Task{ + { + Name: "admin-web", + }, + }, + }, + }, + } + + j.ExpandAllServiceNames() + + service1Name := j.TaskGroups[0].Tasks[0].Services[0].Name + if service1Name != "my-job-web-frontend-default" { + t.Fatalf("Expected Service Name: %s, Actual: %s", "my-job-web-frontend-default", service1Name) + } + + service2Name := j.TaskGroups[0].Tasks[0].Services[1].Name + if service2Name != "jmx" { + t.Fatalf("Expected Service Name: %s, Actual: %s", "jmx", service2Name) + } + +}