feat(ansible): honour task check mode overrides
Some checks are pending
CI / test (push) Waiting to run
CI / auto-fix (push) Waiting to run
CI / auto-merge (push) Waiting to run

This commit is contained in:
Virgil 2026-04-03 13:38:19 +00:00
parent 1b13b33821
commit 80fb75baab
5 changed files with 94 additions and 0 deletions

View file

@ -1032,6 +1032,21 @@ func (e *Executor) runTaskOnHost(ctx context.Context, host string, hosts []strin
e.mu.Unlock() e.mu.Unlock()
}() }()
savedCheckMode := e.CheckMode
savedDiff := e.Diff
if task != nil {
if task.CheckMode != nil {
e.CheckMode = *task.CheckMode
}
if task.Diff != nil {
e.Diff = *task.Diff
}
}
defer func() {
e.CheckMode = savedCheckMode
e.Diff = savedDiff
}()
if e.OnTaskStart != nil { if e.OnTaskStart != nil {
e.OnTaskStart(host, task) e.OnTaskStart(host, task)
} }

View file

@ -32,6 +32,10 @@ func (c *trackingMockClient) SetBecome(become bool, user, password string) {
c.MockSSHClient.SetBecome(become, user, password) c.MockSSHClient.SetBecome(become, user, password)
} }
func boolPtr(v bool) *bool {
return &v
}
// --- NewExecutor --- // --- NewExecutor ---
func TestExecutor_NewExecutor_Good(t *testing.T) { func TestExecutor_NewExecutor_Good(t *testing.T) {
@ -1845,6 +1849,61 @@ func TestExecutor_RunTaskOnHost_Good_CheckModeSkipsMutatingTask(t *testing.T) {
assert.True(t, e.results["host1"]["shell_result"].Skipped) assert.True(t, e.results["host1"]["shell_result"].Skipped)
} }
func TestExecutor_RunTaskOnHost_Good_TaskCheckModeOverridesExecutorCheckMode(t *testing.T) {
e := NewExecutor("/tmp")
e.CheckMode = true
e.SetInventoryDirect(&Inventory{
All: &InventoryGroup{
Hosts: map[string]*Host{
"host1": {},
},
},
})
e.clients["host1"] = NewMockSSHClient()
task := &Task{
Name: "Run despite global check mode",
Module: "shell",
Args: map[string]any{"_raw_params": "echo hello"},
CheckMode: boolPtr(false),
Register: "shell_result",
}
err := e.runTaskOnHost(context.Background(), "host1", []string{"host1"}, task, &Play{})
require.NoError(t, err)
require.NotNil(t, e.results["host1"]["shell_result"])
assert.False(t, e.results["host1"]["shell_result"].Skipped)
assert.True(t, e.results["host1"]["shell_result"].Changed)
}
func TestExecutor_RunTaskOnHost_Good_TaskDiffOverridesExecutorDiff(t *testing.T) {
e := NewExecutor("/tmp")
e.SetInventoryDirect(&Inventory{
All: &InventoryGroup{
Hosts: map[string]*Host{
"host1": {},
},
},
})
e.clients["host1"] = NewMockSSHClient()
task := &Task{
Name: "Inspect task diff mode",
Module: "debug",
Args: map[string]any{"msg": "{{ ansible_diff_mode }}"},
Diff: boolPtr(true),
Register: "diff_result",
}
err := e.runTaskOnHost(context.Background(), "host1", []string{"host1"}, task, &Play{})
require.NoError(t, err)
require.NotNil(t, e.results["host1"])
require.NotNil(t, e.results["host1"]["diff_result"])
assert.Equal(t, "true", e.results["host1"]["diff_result"].Msg)
}
// --- no_log --- // --- no_log ---
func TestExecutor_RunTaskOnHost_Good_NoLogRedactsCallbackResult(t *testing.T) { func TestExecutor_RunTaskOnHost_Good_NoLogRedactsCallbackResult(t *testing.T) {

View file

@ -448,6 +448,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
"loop_control": true, "vars": true, "environment": true, "loop_control": true, "vars": true, "environment": true,
"changed_when": true, "failed_when": true, "ignore_errors": true, "changed_when": true, "failed_when": true, "ignore_errors": true,
"no_log": true, "become": true, "become_user": true, "no_log": true, "become": true, "become_user": true,
"check_mode": true, "diff": true,
"delegate_to": true, "delegate_facts": true, "run_once": true, "tags": true, "delegate_to": true, "delegate_facts": true, "run_once": true, "tags": true,
"block": true, "rescue": true, "always": true, "notify": true, "listen": true, "block": true, "rescue": true, "always": true, "notify": true, "listen": true,
"module_defaults": true, "module_defaults": true,

View file

@ -137,6 +137,8 @@ type Task struct {
Args map[string]any `yaml:"-"` // Module arguments Args map[string]any `yaml:"-"` // Module arguments
Register string `yaml:"register,omitempty"` Register string `yaml:"register,omitempty"`
When any `yaml:"when,omitempty"` // string or []string When any `yaml:"when,omitempty"` // string or []string
CheckMode *bool `yaml:"check_mode,omitempty"`
Diff *bool `yaml:"diff,omitempty"`
Loop any `yaml:"loop,omitempty"` // string or []any Loop any `yaml:"loop,omitempty"` // string or []any
LoopControl *LoopControl `yaml:"loop_control,omitempty"` LoopControl *LoopControl `yaml:"loop_control,omitempty"`
Vars map[string]any `yaml:"vars,omitempty"` Vars map[string]any `yaml:"vars,omitempty"`

View file

@ -156,6 +156,23 @@ when: some_var is defined
assert.NotNil(t, task.When) assert.NotNil(t, task.When)
} }
func TestTypes_Task_UnmarshalYAML_Good_WithCheckModeAndDiff(t *testing.T) {
input := `
name: Force a dry run
shell: echo hello
check_mode: false
diff: true
`
var task Task
err := yaml.Unmarshal([]byte(input), &task)
require.NoError(t, err)
require.NotNil(t, task.CheckMode)
require.NotNil(t, task.Diff)
assert.False(t, *task.CheckMode)
assert.True(t, *task.Diff)
}
func TestTypes_Task_UnmarshalYAML_Good_WithLoop(t *testing.T) { func TestTypes_Task_UnmarshalYAML_Good_WithLoop(t *testing.T) {
input := ` input := `
name: Install packages name: Install packages