Files
nomad/client/allocrunner/taskrunner/testing/testing.go
Tim Gross cc9227b858 template: fix panic in change_mode=script on client restart (#24057)
When we introduced change_mode=script to templates, we passed the driver handle
down into the template manager so we could call its `Exec` method directly. But
the lifecycle of the driver handle is managed by the taskrunner and isn't
available when the template manager is first created. This has led to a series
of patches trying to fixup the behavior (#15915, #15192, #23663, #23917). Part
of the challenge in getting this right is using an interface to avoid the
circular import of the driver handle.

But the taskrunner already has a way to deal with this problem using a "lazy
handle". The other template change modes already use this indirectly through the
`Lifecycle` interface. Change the driver handle `Exec` call in the template
manager to a new `Lifecycle.Exec` call that reuses the existing behavior. This
eliminates the need for the template manager to know anything at all about the
handle state.

Fixes: https://github.com/hashicorp/nomad/issues/24051
2024-09-25 08:59:01 -04:00

162 lines
3.1 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package testing
import (
"context"
"sync"
"time"
"github.com/hashicorp/nomad/nomad/structs"
)
// MockEmitter is a mock of the EventEmitter interface.
type MockEmitter struct {
lock sync.Mutex
events []*structs.TaskEvent
}
func (m *MockEmitter) EmitEvent(ev *structs.TaskEvent) {
m.lock.Lock()
defer m.lock.Unlock()
m.events = append(m.events, ev)
}
func (m *MockEmitter) Events() []*structs.TaskEvent {
m.lock.Lock()
defer m.lock.Unlock()
return m.events
}
// MockTaskHooks is a mock of the TaskHooks interface useful for testing
type MockTaskHooks struct {
lock sync.Mutex
RestartCh chan struct{}
restarts int
SignalCh chan struct{}
signals []string
// SignalError is returned when Signal is called on the mock hook
SignalError error
UnblockCh chan struct{}
KillCh chan *structs.TaskEvent
killEvent *structs.TaskEvent
EmitEventCh chan *structs.TaskEvent
events []*structs.TaskEvent
execCode int
execErr error
// HasHandle can be set to simulate restoring a task after client restart
HasHandle bool
}
func NewMockTaskHooks() *MockTaskHooks {
return &MockTaskHooks{
UnblockCh: make(chan struct{}, 1),
RestartCh: make(chan struct{}, 1),
SignalCh: make(chan struct{}, 1),
KillCh: make(chan *structs.TaskEvent, 1),
EmitEventCh: make(chan *structs.TaskEvent, 1),
}
}
func (m *MockTaskHooks) Restart(ctx context.Context, event *structs.TaskEvent, failure bool) error {
m.lock.Lock()
defer m.lock.Unlock()
m.restarts++
select {
case m.RestartCh <- struct{}{}:
default:
}
return nil
}
func (m *MockTaskHooks) Signal(event *structs.TaskEvent, s string) error {
m.lock.Lock()
m.signals = append(m.signals, s)
m.lock.Unlock()
select {
case m.SignalCh <- struct{}{}:
default:
}
return m.SignalError
}
func (m *MockTaskHooks) Signals() []string {
m.lock.Lock()
defer m.lock.Unlock()
return m.signals
}
func (m *MockTaskHooks) Kill(ctx context.Context, event *structs.TaskEvent) error {
m.lock.Lock()
defer m.lock.Unlock()
m.killEvent = event
select {
case m.KillCh <- event:
default:
}
return nil
}
func (m *MockTaskHooks) Exec(timeout time.Duration, cmd string, args []string) ([]byte, int, error) {
m.lock.Lock()
defer m.lock.Unlock()
return []byte{}, m.execCode, m.execErr
}
func (m *MockTaskHooks) SetupExecTest(code int, err error) {
m.lock.Lock()
defer m.lock.Unlock()
m.execCode = code
m.execErr = err
}
func (m *MockTaskHooks) IsRunning() bool {
return m.HasHandle
}
func (m *MockTaskHooks) EmitEvent(event *structs.TaskEvent) {
m.lock.Lock()
defer m.lock.Unlock()
m.events = append(m.events, event)
select {
case m.EmitEventCh <- event:
case <-m.EmitEventCh:
m.EmitEventCh <- event
}
}
func (m *MockTaskHooks) SetState(state string, event *structs.TaskEvent) {}
func (m *MockTaskHooks) KillEvent() *structs.TaskEvent {
m.lock.Lock()
defer m.lock.Unlock()
return m.killEvent
}
func (m *MockTaskHooks) Events() []*structs.TaskEvent {
m.lock.Lock()
defer m.lock.Unlock()
return m.events
}
func (m *MockTaskHooks) Restarts() int {
m.lock.Lock()
defer m.lock.Unlock()
return m.restarts
}