-
Notifications
You must be signed in to change notification settings - Fork 2k
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: fix panic in change_mode=script on client restart #24057
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
```release-note:bug | ||
template: Fixed a panic on client restart when using change_mode=script | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -60,10 +60,6 @@ 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 | ||
|
@@ -220,14 +216,6 @@ 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 | ||
|
||
} | ||
|
||
// 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 | ||
|
@@ -583,21 +571,10 @@ func (tm *TaskTemplateManager) handleScriptError(script *structs.ChangeScript, m | |
func (tm *TaskTemplateManager) processScript(script *structs.ChangeScript, wg *sync.WaitGroup) { | ||
defer wg.Done() | ||
|
||
tm.handleLock.Lock() | ||
defer tm.handleLock.Unlock() | ||
if tm.handle == nil { | ||
failureMsg := fmt.Sprintf( | ||
"Template failed to run script %v with arguments %v because task driver handle is not available", | ||
script.Command, | ||
script.Args, | ||
) | ||
tm.handleScriptError(script, failureMsg) | ||
return | ||
} | ||
_, exitCode, err := tm.handle.Exec(script.Timeout, script.Command, script.Args) | ||
_, exitCode, err := tm.config.Lifecycle.Exec(script.Timeout, script.Command, script.Args) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this feels so much nicer 👏 |
||
if err != nil { | ||
failureMsg := fmt.Sprintf( | ||
"Template failed to run script %v with arguments %v on change: %v Exit code: %v", | ||
"Template failed to run script %v with arguments %v on change: %v. Exit code: %v", | ||
script.Command, | ||
script.Args, | ||
err, | ||
|
@@ -608,7 +585,7 @@ func (tm *TaskTemplateManager) processScript(script *structs.ChangeScript, wg *s | |
} | ||
if exitCode != 0 { | ||
failureMsg := fmt.Sprintf( | ||
"Template ran script %v with arguments %v on change but it exited with code code: %v", | ||
"Template ran script %v with arguments %v on change but it exited with code: %v", | ||
script.Command, | ||
script.Args, | ||
exitCode, | ||
|
@@ -619,10 +596,9 @@ func (tm *TaskTemplateManager) processScript(script *structs.ChangeScript, wg *s | |
tm.config.Events.EmitEvent(structs.NewTaskEvent(structs.TaskHookMessage). | ||
SetDisplayMessage( | ||
fmt.Sprintf( | ||
"Template successfully ran script %v with arguments: %v. Exit code: %v", | ||
"Template successfully ran script %v with arguments: %v. Exit code: 0", | ||
script.Command, | ||
script.Args, | ||
exitCode, | ||
))) | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,16 +59,6 @@ const ( | |
TestTaskName = "test-task" | ||
) | ||
|
||
// mockExecutor implements script executor interface | ||
type mockExecutor struct { | ||
DesiredExit int | ||
DesiredErr error | ||
} | ||
|
||
func (m *mockExecutor) Exec(timeout time.Duration, cmd string, args []string) ([]byte, int, error) { | ||
return []byte{}, m.DesiredExit, m.DesiredErr | ||
} | ||
|
||
// testHarness is used to test the TaskTemplateManager by spinning up | ||
// Consul/Vault as needed | ||
type testHarness struct { | ||
|
@@ -1146,7 +1136,7 @@ func TestTaskTemplateManager_ScriptExecution(t *testing.T) { | |
key2 := "bar" | ||
content1_1 := "cat" | ||
content1_2 := "dog" | ||
content1_3 := "goldfish" | ||
|
||
t1 := &structs.Template{ | ||
EmbeddedTmpl: ` | ||
FOO={{key "bam"}} | ||
|
@@ -1176,10 +1166,9 @@ BAR={{key "bar"}} | |
Envvars: true, | ||
} | ||
|
||
me := mockExecutor{DesiredExit: 0, DesiredErr: nil} | ||
harness := newTestHarness(t, []*structs.Template{t1, t2}, true, false) | ||
harness.mockHooks.SetupExecTest(0, nil) | ||
harness.start(t) | ||
harness.manager.SetDriverHandle(&me) | ||
defer harness.stop() | ||
|
||
// Ensure no unblock | ||
|
@@ -1220,29 +1209,6 @@ OUTER: | |
t.Fatal(t, "should have received an event") | ||
} | ||
} | ||
|
||
// remove the reference to the task handle and update the template contents | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note to reviewers: this is the test we added in #23917 that we knew even at the time didn't really test anything useful. Now that we've fixed the problem by fixing the architecture, this doesn't even have anything left to test so I'm removing it. There's no way to "remove the task handle" anymore, as the template manager doesn't have a reference to it at all. The code for this PR is covered in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. makes sense to remove this, but I don't see a test file called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, that's a typo, it's this test: https://github.com/hashicorp/nomad/blob/main/client/allocrunner/taskrunner/template_hook_test.go#L277 Note that only an integration test that spins up a real task and can restart a client can actually exercise this bug. So that falls under the general pile of "things we'd really like to do when we have solid E2E upgrade/restart testing". |
||
// again | ||
harness.manager.SetDriverHandle(nil) | ||
harness.consul.SetKV(t, key1, []byte(content1_3)) | ||
timeout = time.After(time.Duration(5*testutil.TestMultiplier()) * time.Second) | ||
|
||
OUTER2: | ||
for { | ||
select { | ||
case <-harness.mockHooks.RestartCh: | ||
t.Fatal(t, "restart not expected") | ||
case ev := <-harness.mockHooks.EmitEventCh: | ||
if strings.Contains( | ||
ev.DisplayMessage, "task driver handle is not available") { | ||
break OUTER2 | ||
} | ||
case <-harness.mockHooks.SignalCh: | ||
t.Fatal(t, "signal not expected") | ||
case <-timeout: | ||
t.Fatal(t, "should have received an event that task driver handle is unavailable") | ||
} | ||
} | ||
} | ||
|
||
// TestTaskTemplateManager_ScriptExecutionFailTask tests whether we fail the | ||
|
@@ -1285,10 +1251,9 @@ BAR={{key "bar"}} | |
Envvars: true, | ||
} | ||
|
||
me := mockExecutor{DesiredExit: 1, DesiredErr: fmt.Errorf("Script failed")} | ||
harness := newTestHarness(t, []*structs.Template{t1, t2}, true, false) | ||
harness.mockHooks.SetupExecTest(1, fmt.Errorf("Script failed")) | ||
harness.start(t) | ||
harness.manager.SetDriverHandle(&me) | ||
defer harness.stop() | ||
|
||
// Ensure no unblock | ||
|
@@ -1365,10 +1330,9 @@ COMMON={{key "common"}} | |
templateScript, | ||
} | ||
|
||
me := mockExecutor{DesiredExit: 0, DesiredErr: nil} | ||
harness := newTestHarness(t, templates, true, false) | ||
harness.mockHooks.SetupExecTest(0, nil) | ||
harness.start(t) | ||
harness.manager.SetDriverHandle(&me) | ||
defer harness.stop() | ||
|
||
// Ensure no unblock | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These changes aren't really necessary now, but they'll provide belt-and-suspenders against future bugs.