Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

template: custom change_mode scripts #13972

Merged
merged 71 commits into from
Aug 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
aa4e3a4
TemplateChangeModeScript
pkazmierczak Aug 3, 2022
cb79fb1
updated documentation
pkazmierczak Aug 4, 2022
cf3dffa
changelog entry
pkazmierczak Aug 4, 2022
03e6dab
wip
pkazmierczak Aug 4, 2022
3f2b1ec
handle script execution
pkazmierczak Aug 4, 2022
634e2d4
documentation
pkazmierczak Aug 4, 2022
f7a53cb
timeout can just be time.Duration
pkazmierczak Aug 4, 2022
56d0113
tasks & jobspec update
pkazmierczak Aug 4, 2022
efbd9cf
typos
pkazmierczak Aug 4, 2022
fbee6a2
adjusted tests
pkazmierczak Aug 5, 2022
5aa40b5
wip
pkazmierczak Aug 8, 2022
a8fcaba
corrected diff_test
pkazmierczak Aug 8, 2022
a2dd176
applied Derek's comments
pkazmierczak Aug 8, 2022
ae15843
corrections
pkazmierczak Aug 9, 2022
f9b12c6
revamped tests
pkazmierczak Aug 9, 2022
55d33da
revamped test
pkazmierczak Aug 9, 2022
611738b
Refactor test to listen for event channel
DerekStrickland Aug 9, 2022
1277dd6
diffs corrected
pkazmierczak Aug 10, 2022
ac8d910
CI-driven debugging :/
pkazmierczak Aug 10, 2022
2270765
fixed test
pkazmierczak Aug 10, 2022
8d61a25
FailTask
pkazmierczak Aug 10, 2022
4839c67
s/suck/such;
pkazmierczak Aug 10, 2022
5f98282
jobs_test correction
pkazmierczak Aug 11, 2022
8a6d591
documentation
pkazmierczak Aug 11, 2022
21f15ad
typo in the docs
pkazmierczak Aug 11, 2022
1f8b044
test FailTask
pkazmierczak Aug 11, 2022
b8f2ba8
Merge branch 'main' into f-nomad-template-custom-change-mode
pkazmierczak Aug 11, 2022
bcd7213
fail_on_error
pkazmierczak Aug 11, 2022
de9c7c2
addressed Derek's comments
pkazmierczak Aug 11, 2022
986a018
updated json-jobs.mdx
pkazmierczak Aug 11, 2022
df9f13b
typo in diff_test
pkazmierczak Aug 11, 2022
6ada97e
removed obsolete documentation
pkazmierczak Aug 11, 2022
d8fd77b
better canonicalization
pkazmierczak Aug 11, 2022
4673f95
make timeout optional
pkazmierczak Aug 11, 2022
f8f4fdd
hcl parser fixes
pkazmierczak Aug 11, 2022
3b68123
canonicalize test isn't needed
pkazmierczak Aug 11, 2022
87fd05f
pointer non-sense
pkazmierczak Aug 12, 2022
3d1ebdc
fixed conditional
pkazmierczak Aug 12, 2022
675a667
set poststart for template hook
pkazmierczak Aug 16, 2022
ce6f351
nil check
pkazmierczak Aug 16, 2022
9c25095
fixed test
pkazmierczak Aug 16, 2022
13b6f75
fixed tests
pkazmierczak Aug 16, 2022
2266453
Update website/content/api-docs/json-jobs.mdx
pkazmierczak Aug 18, 2022
010d8a1
Update website/content/api-docs/json-jobs.mdx
pkazmierczak Aug 18, 2022
ee50b9d
Update website/content/api-docs/json-jobs.mdx
pkazmierczak Aug 18, 2022
f834e8d
Merge branch 'main' into f-nomad-template-custom-change-mode
pkazmierczak Aug 18, 2022
de7156f
"path" documentation
pkazmierczak Aug 18, 2022
726397c
fail task in the poststart if template change mode is script and driv…
pkazmierczak Aug 18, 2022
67eda32
change script config -> change script
pkazmierczak Aug 18, 2022
0553d09
removed obsolete ioutil dependency
pkazmierczak Aug 18, 2022
a63d77c
execute scripts concurrently
pkazmierczak Aug 18, 2022
b4d4ec8
mutex for driver handle
pkazmierczak Aug 18, 2022
c97ea30
refactored onTemplateRendered
pkazmierczak Aug 18, 2022
944eeee
path -> command
pkazmierczak Aug 18, 2022
4b7b306
another script example in the docs
pkazmierczak Aug 19, 2022
b22d74f
simplified script execution processing
pkazmierczak Aug 19, 2022
f1050e1
handle exit code
pkazmierczak Aug 19, 2022
6190282
fix diff_test
pkazmierczak Aug 19, 2022
953c091
allowed_exit_codes
pkazmierczak Aug 19, 2022
4add2b0
Revert "allowed_exit_codes"
pkazmierczak Aug 19, 2022
f9daf96
simplified processScript
pkazmierczak Aug 19, 2022
b0e7181
Apply suggestions from code review
pkazmierczak Aug 23, 2022
1da69c1
Merge branch 'main' into f-nomad-template-custom-change-mode
pkazmierczak Aug 23, 2022
b9d0da2
refactored processScript
pkazmierczak Aug 23, 2022
d8c4cda
test fix
pkazmierczak Aug 23, 2022
a2a855e
nolint
pkazmierczak Aug 23, 2022
9629e69
removed obsolete code
pkazmierczak Aug 23, 2022
b77da29
Apply suggestions from code review
pkazmierczak Aug 24, 2022
0a1b5d7
Update website/content/api-docs/json-jobs.mdx
pkazmierczak Aug 24, 2022
88993bb
formatting
pkazmierczak Aug 24, 2022
96856ba
removed obsolete code
pkazmierczak Aug 24, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/13972.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:improvement
template: add script change_mode that allows scripts to be executed on template change
```
26 changes: 26 additions & 0 deletions api/tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -809,11 +809,34 @@ func (wc *WaitConfig) Copy() *WaitConfig {
return nwc
}

type ChangeScript struct {
Command *string `mapstructure:"command" hcl:"command"`
Args []string `mapstructure:"args" hcl:"args,optional"`
Timeout *time.Duration `mapstructure:"timeout" hcl:"timeout,optional"`
FailOnError *bool `mapstructure:"fail_on_error" hcl:"fail_on_error"`
}

func (ch *ChangeScript) Canonicalize() {
if ch.Command == nil {
ch.Command = pointerOf("")
}
if ch.Args == nil {
ch.Args = []string{}
}
if ch.Timeout == nil {
ch.Timeout = pointerOf(5 * time.Second)
}
if ch.FailOnError == nil {
ch.FailOnError = pointerOf(false)
}
}

type Template struct {
SourcePath *string `mapstructure:"source" hcl:"source,optional"`
DestPath *string `mapstructure:"destination" hcl:"destination,optional"`
EmbeddedTmpl *string `mapstructure:"data" hcl:"data,optional"`
ChangeMode *string `mapstructure:"change_mode" hcl:"change_mode,optional"`
ChangeScript *ChangeScript `mapstructure:"change_script" hcl:"change_script,block"`
ChangeSignal *string `mapstructure:"change_signal" hcl:"change_signal,optional"`
Splay *time.Duration `mapstructure:"splay" hcl:"splay,optional"`
Perms *string `mapstructure:"perms" hcl:"perms,optional"`
Expand Down Expand Up @@ -849,6 +872,9 @@ func (tmpl *Template) Canonicalize() {
sig := *tmpl.ChangeSignal
tmpl.ChangeSignal = pointerOf(strings.ToUpper(sig))
}
if tmpl.ChangeScript != nil {
tmpl.ChangeScript.Canonicalize()
}
if tmpl.Splay == nil {
tmpl.Splay = pointerOf(5 * time.Second)
}
Expand Down
81 changes: 81 additions & 0 deletions client/allocrunner/taskrunner/template/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ type TaskTemplateManager struct {
// runner is the consul-template runner
runner *manager.Runner

// handle is used to execute scripts
handle interfaces.ScriptExecutor
handleLock sync.Mutex

// signals is a lookup map from the string representation of a signal to its
// actual signal
signals map[string]os.Signal
Expand Down Expand Up @@ -192,6 +196,14 @@ func (tm *TaskTemplateManager) Stop() {
}
}

// SetDriverHandle sets the executor
func (tm *TaskTemplateManager) SetDriverHandle(executor interfaces.ScriptExecutor) {
tm.handleLock.Lock()
defer tm.handleLock.Unlock()
tm.handle = executor
pkazmierczak marked this conversation as resolved.
Show resolved Hide resolved

}

// run is the long lived loop that handles errors and templates being rendered
func (tm *TaskTemplateManager) run() {
// Runner is nil if there are no templates
Expand Down Expand Up @@ -392,6 +404,7 @@ func (tm *TaskTemplateManager) onTemplateRendered(handledRenders map[string]time

var handling []string
signals := make(map[string]struct{})
scripts := []*structs.ChangeScript{}
restart := false
var splay time.Duration

Expand Down Expand Up @@ -436,6 +449,8 @@ func (tm *TaskTemplateManager) onTemplateRendered(handledRenders map[string]time
signals[tmpl.ChangeSignal] = struct{}{}
case structs.TemplateChangeModeRestart:
restart = true
case structs.TemplateChangeModeScript:
scripts = append(scripts, tmpl.ChangeScript)
case structs.TemplateChangeModeNoop:
continue
}
Expand Down Expand Up @@ -494,6 +509,72 @@ func (tm *TaskTemplateManager) onTemplateRendered(handledRenders map[string]time
}
}

// process script execution concurrently
var wg sync.WaitGroup
for _, script := range scripts {
wg.Add(1)
go tm.processScript(script, &wg)
}
wg.Wait()
}

// handleScriptError is a helper function that produces a TaskKilling event and
// emits a message
func (tm *TaskTemplateManager) handleScriptError(script *structs.ChangeScript, msg string) {
ev := structs.NewTaskEvent(structs.TaskHookFailed).SetDisplayMessage(msg)
tm.config.Events.EmitEvent(ev)

if script.FailOnError {
tm.config.Lifecycle.Kill(context.Background(),
structs.NewTaskEvent(structs.TaskKilling).
SetFailsTask().
SetDisplayMessage("Template script failed, task is being killed"))
pkazmierczak marked this conversation as resolved.
Show resolved Hide resolved
}
}

// processScript is used for executing change_mode script and handling errors
func (tm *TaskTemplateManager) processScript(script *structs.ChangeScript, wg *sync.WaitGroup) {
defer wg.Done()

if tm.handle == nil {
failureMsg := fmt.Sprintf(
"Template failed to run script %v with arguments %v because task driver doesn't support the exec operation",
script.Command,
script.Args,
)
tm.handleScriptError(script, failureMsg)
return
}
_, exitCode, err := tm.handle.Exec(script.Timeout, script.Command, script.Args)
if err != nil {
failureMsg := fmt.Sprintf(
"Template failed to run script %v with arguments %v on change: %v Exit code: %v",
script.Command,
script.Args,
err,
exitCode,
)
tm.handleScriptError(script, failureMsg)
return
}
if exitCode != 0 {
failureMsg := fmt.Sprintf(
"Template ran script %v with arguments %v on change but it exited with code code: %v",
script.Command,
script.Args,
exitCode,
)
tm.handleScriptError(script, failureMsg)
return
}
tm.config.Events.EmitEvent(structs.NewTaskEvent(structs.TaskHookMessage).
SetDisplayMessage(
fmt.Sprintf(
"Template successfully ran script %v with arguments: %v. Exit code: %v",
script.Command,
script.Args,
exitCode,
)))
}

// allTemplatesNoop returns whether all the managed templates have change mode noop.
Expand Down
Loading