Add play module defaults support
This commit is contained in:
parent
bbe110c1c0
commit
031e41be19
4 changed files with 120 additions and 23 deletions
|
|
@ -1403,6 +1403,49 @@ func TestExecutorExtra_RunIncludeRole_Good_PublicVarsPersist(t *testing.T) {
|
|||
assert.Equal(t, "hello from public role", e.results["localhost"]["after_public_role"].Msg)
|
||||
}
|
||||
|
||||
func TestExecutorExtra_RunPlay_Good_ModuleDefaultsApplyToTasks(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
e := NewExecutor(dir)
|
||||
e.SetInventoryDirect(&Inventory{
|
||||
All: &InventoryGroup{
|
||||
Hosts: map[string]*Host{
|
||||
"localhost": {},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
mock := newTrackingMockClient()
|
||||
e.clients["localhost"] = mock
|
||||
mock.expectCommand(`cd "/tmp/module-defaults" && pwd`, "/tmp/module-defaults\n", "", 0)
|
||||
|
||||
gatherFacts := false
|
||||
play := &Play{
|
||||
Name: "Module defaults",
|
||||
Hosts: "localhost",
|
||||
Connection: "local",
|
||||
GatherFacts: &gatherFacts,
|
||||
ModuleDefaults: map[string]map[string]any{
|
||||
"command": {
|
||||
"chdir": "/tmp/module-defaults",
|
||||
},
|
||||
},
|
||||
Tasks: []Task{
|
||||
{
|
||||
Name: "Run command with defaults",
|
||||
Module: "command",
|
||||
Args: map[string]any{"cmd": "pwd"},
|
||||
Register: "command_result",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
require.NoError(t, e.runPlay(context.Background(), play))
|
||||
|
||||
require.NotNil(t, e.results["localhost"]["command_result"])
|
||||
assert.Equal(t, "/tmp/module-defaults\n", e.results["localhost"]["command_result"].Stdout)
|
||||
assert.True(t, mock.hasExecuted(`cd "/tmp/module-defaults" && pwd`))
|
||||
}
|
||||
|
||||
func TestExecutorExtra_GetHostsIter_Good(t *testing.T) {
|
||||
inv := &Inventory{
|
||||
All: &InventoryGroup{
|
||||
|
|
|
|||
56
modules.go
56
modules.go
|
|
@ -67,8 +67,10 @@ func (e *Executor) executeModule(ctx context.Context, host string, client sshExe
|
|||
}
|
||||
}
|
||||
|
||||
// Template the args
|
||||
args := e.templateArgs(task.Args, host, task)
|
||||
// Merge play-level module defaults before templating so defaults and task
|
||||
// arguments can both resolve host-scoped variables.
|
||||
args := mergeModuleDefaults(task.Args, e.resolveModuleDefaults(play, module))
|
||||
args = e.templateArgs(args, host, task)
|
||||
|
||||
switch module {
|
||||
// Command execution
|
||||
|
|
@ -199,6 +201,56 @@ func (e *Executor) executeModule(ctx context.Context, host string, client sshExe
|
|||
}
|
||||
}
|
||||
|
||||
func (e *Executor) resolveModuleDefaults(play *Play, module string) map[string]any {
|
||||
if play == nil || len(play.ModuleDefaults) == 0 || module == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
canonical := NormalizeModule(module)
|
||||
|
||||
merged := make(map[string]any)
|
||||
seen := false
|
||||
keys := make([]string, 0, len(play.ModuleDefaults))
|
||||
for key := range play.ModuleDefaults {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
if NormalizeModule(key) != canonical {
|
||||
continue
|
||||
}
|
||||
defaults := play.ModuleDefaults[key]
|
||||
if len(defaults) == 0 {
|
||||
continue
|
||||
}
|
||||
for k, v := range defaults {
|
||||
merged[k] = v
|
||||
}
|
||||
seen = true
|
||||
}
|
||||
|
||||
if !seen {
|
||||
return nil
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
func mergeModuleDefaults(args, defaults map[string]any) map[string]any {
|
||||
if len(args) == 0 && len(defaults) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
merged := make(map[string]any, len(args)+len(defaults))
|
||||
for k, v := range defaults {
|
||||
merged[k] = v
|
||||
}
|
||||
for k, v := range args {
|
||||
merged[k] = v
|
||||
}
|
||||
return merged
|
||||
}
|
||||
|
||||
func (e *Executor) resolveBecomePassword(host string) string {
|
||||
if e == nil {
|
||||
return ""
|
||||
|
|
|
|||
|
|
@ -450,6 +450,7 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
|||
"no_log": true, "become": true, "become_user": true,
|
||||
"delegate_to": true, "delegate_facts": true, "run_once": true, "tags": true,
|
||||
"block": true, "rescue": true, "always": true, "notify": true, "listen": true,
|
||||
"module_defaults": true,
|
||||
"retries": true, "delay": true, "until": true,
|
||||
"action": true, "local_action": true,
|
||||
"ansible.builtin.action": true, "ansible.legacy.action": true,
|
||||
|
|
|
|||
1
types.go
1
types.go
|
|
@ -33,6 +33,7 @@ type Play struct {
|
|||
AnyErrorsFatal bool `yaml:"any_errors_fatal,omitempty"`
|
||||
Vars map[string]any `yaml:"vars,omitempty"`
|
||||
VarsFiles any `yaml:"vars_files,omitempty"` // string or []string
|
||||
ModuleDefaults map[string]map[string]any `yaml:"module_defaults,omitempty"`
|
||||
PreTasks []Task `yaml:"pre_tasks,omitempty"`
|
||||
Tasks []Task `yaml:"tasks,omitempty"`
|
||||
PostTasks []Task `yaml:"post_tasks,omitempty"`
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue