diff --git a/command/agent/job_endpoint.go b/command/agent/job_endpoint.go index 9f5b4b1ed..b1432fa3c 100644 --- a/command/agent/job_endpoint.go +++ b/command/agent/job_endpoint.go @@ -924,6 +924,13 @@ func ApiTaskToStructsTask(apiTask *api.Task, structsTask *structs.Task) { File: apiTask.DispatchPayload.File, } } + + if apiTask.Lifecycle != nil { + structsTask.Lifecycle = &structs.TaskLifecycleConfig{ + RunLevel: apiTask.Lifecycle.RunLevel, + BlockUntil: apiTask.Lifecycle.BlockUntil, + } + } } func ApiResourcesToStructs(in *api.Resources) *structs.Resources { diff --git a/jobspec/parse_test.go b/jobspec/parse_test.go index 580790218..56fcd8126 100644 --- a/jobspec/parse_test.go +++ b/jobspec/parse_test.go @@ -340,7 +340,7 @@ func TestParse(t *testing.T) { Driver: "docker", User: "", Lifecycle: &api.TaskLifecycle{ - Runlevel: "prestart", + RunLevel: "prestart", BlockUntil: "completed", }, Config: map[string]interface{}{ diff --git a/nomad/structs/structs.go b/nomad/structs/structs.go index 49d81fb34..8b0980af0 100644 --- a/nomad/structs/structs.go +++ b/nomad/structs/structs.go @@ -25,8 +25,8 @@ import ( "github.com/gorhill/cronexpr" hcodec "github.com/hashicorp/go-msgpack/codec" - "github.com/hashicorp/go-multierror" - "github.com/hashicorp/go-version" + multierror "github.com/hashicorp/go-multierror" + version "github.com/hashicorp/go-version" "github.com/hashicorp/nomad/acl" "github.com/hashicorp/nomad/command/agent/pprof" "github.com/hashicorp/nomad/helper" @@ -4385,6 +4385,46 @@ func (d *DispatchPayloadConfig) Validate() error { return nil } +const ( + TaskLifecycleRunLevelPrestart = "prestart" + TaskLifecycleBlockUntilStarted = "started" + TaskLifecycleBlockUntilCompleted = "completed" +) + +type TaskLifecycleConfig struct { + RunLevel string + BlockUntil string +} + +func (d *TaskLifecycleConfig) Copy() *TaskLifecycleConfig { + if d == nil { + return nil + } + nd := new(TaskLifecycleConfig) + *nd = *d + return nd +} + +func (d *TaskLifecycleConfig) Validate() error { + if d == nil { + return nil + } + + switch d.RunLevel { + case TaskLifecycleRunLevelPrestart: + default: + return fmt.Errorf("invalid run_level: %v", d.RunLevel) + } + + switch d.BlockUntil { + case TaskLifecycleBlockUntilStarted, TaskLifecycleBlockUntilCompleted: + default: + return fmt.Errorf("invalid block_until: %v", d.BlockUntil) + } + + return nil +} + var ( // These default restart policies needs to be in sync with // Canonicalize in api/tasks.go @@ -5407,6 +5447,8 @@ type Task struct { // DispatchPayload configures how the task retrieves its input from a dispatch DispatchPayload *DispatchPayloadConfig + Lifecycle *TaskLifecycleConfig + // Meta is used to associate arbitrary metadata with this // task. This is opaque to Nomad. Meta map[string]string @@ -5486,6 +5528,7 @@ func (t *Task) Copy() *Task { nt.LogConfig = nt.LogConfig.Copy() nt.Meta = helper.CopyMapStringString(nt.Meta) nt.DispatchPayload = nt.DispatchPayload.Copy() + nt.Lifecycle = nt.Lifecycle.Copy() if t.Artifacts != nil { artifacts := make([]*TaskArtifact, 0, len(t.Artifacts)) @@ -5665,6 +5708,14 @@ func (t *Task) Validate(ephemeralDisk *EphemeralDisk, jobType string, tgServices } } + // Validate the Lifecycle block if there + if t.Lifecycle != nil { + if err := t.Lifecycle.Validate(); err != nil { + mErr.Errors = append(mErr.Errors, fmt.Errorf("Lifecycle validation failed: %v", err)) + } + + } + // Validation for TaskKind field which is used for Consul Connect integration if t.Kind.IsConnectProxy() { // This task is a Connect proxy so it should not have service stanzas