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)
|
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) {
|
func TestExecutorExtra_GetHostsIter_Good(t *testing.T) {
|
||||||
inv := &Inventory{
|
inv := &Inventory{
|
||||||
All: &InventoryGroup{
|
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
|
// Merge play-level module defaults before templating so defaults and task
|
||||||
args := e.templateArgs(task.Args, host, task)
|
// arguments can both resolve host-scoped variables.
|
||||||
|
args := mergeModuleDefaults(task.Args, e.resolveModuleDefaults(play, module))
|
||||||
|
args = e.templateArgs(args, host, task)
|
||||||
|
|
||||||
switch module {
|
switch module {
|
||||||
// Command execution
|
// 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 {
|
func (e *Executor) resolveBecomePassword(host string) string {
|
||||||
if e == nil {
|
if e == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|
|
||||||
|
|
@ -450,7 +450,8 @@ func (t *Task) UnmarshalYAML(node *yaml.Node) error {
|
||||||
"no_log": true, "become": true, "become_user": true,
|
"no_log": true, "become": true, "become_user": 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,
|
||||||
"retries": true, "delay": true, "until": true,
|
"module_defaults": true,
|
||||||
|
"retries": true, "delay": true, "until": true,
|
||||||
"action": true, "local_action": true,
|
"action": true, "local_action": true,
|
||||||
"ansible.builtin.action": true, "ansible.legacy.action": true,
|
"ansible.builtin.action": true, "ansible.legacy.action": true,
|
||||||
"ansible.builtin.local_action": true, "ansible.legacy.local_action": true,
|
"ansible.builtin.local_action": true, "ansible.legacy.local_action": true,
|
||||||
|
|
|
||||||
41
types.go
41
types.go
|
|
@ -22,26 +22,27 @@ type Playbook struct {
|
||||||
//
|
//
|
||||||
// play := Play{Name: "Configure web", Hosts: "webservers", Become: true}
|
// play := Play{Name: "Configure web", Hosts: "webservers", Become: true}
|
||||||
type Play struct {
|
type Play struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Hosts string `yaml:"hosts"`
|
Hosts string `yaml:"hosts"`
|
||||||
ImportPlaybook string `yaml:"import_playbook,omitempty"`
|
ImportPlaybook string `yaml:"import_playbook,omitempty"`
|
||||||
Connection string `yaml:"connection,omitempty"`
|
Connection string `yaml:"connection,omitempty"`
|
||||||
Become bool `yaml:"become,omitempty"`
|
Become bool `yaml:"become,omitempty"`
|
||||||
BecomeUser string `yaml:"become_user,omitempty"`
|
BecomeUser string `yaml:"become_user,omitempty"`
|
||||||
GatherFacts *bool `yaml:"gather_facts,omitempty"`
|
GatherFacts *bool `yaml:"gather_facts,omitempty"`
|
||||||
ForceHandlers bool `yaml:"force_handlers,omitempty"`
|
ForceHandlers bool `yaml:"force_handlers,omitempty"`
|
||||||
AnyErrorsFatal bool `yaml:"any_errors_fatal,omitempty"`
|
AnyErrorsFatal bool `yaml:"any_errors_fatal,omitempty"`
|
||||||
Vars map[string]any `yaml:"vars,omitempty"`
|
Vars map[string]any `yaml:"vars,omitempty"`
|
||||||
VarsFiles any `yaml:"vars_files,omitempty"` // string or []string
|
VarsFiles any `yaml:"vars_files,omitempty"` // string or []string
|
||||||
PreTasks []Task `yaml:"pre_tasks,omitempty"`
|
ModuleDefaults map[string]map[string]any `yaml:"module_defaults,omitempty"`
|
||||||
Tasks []Task `yaml:"tasks,omitempty"`
|
PreTasks []Task `yaml:"pre_tasks,omitempty"`
|
||||||
PostTasks []Task `yaml:"post_tasks,omitempty"`
|
Tasks []Task `yaml:"tasks,omitempty"`
|
||||||
Roles []RoleRef `yaml:"roles,omitempty"`
|
PostTasks []Task `yaml:"post_tasks,omitempty"`
|
||||||
Handlers []Task `yaml:"handlers,omitempty"`
|
Roles []RoleRef `yaml:"roles,omitempty"`
|
||||||
Tags []string `yaml:"tags,omitempty"`
|
Handlers []Task `yaml:"handlers,omitempty"`
|
||||||
Environment map[string]string `yaml:"environment,omitempty"`
|
Tags []string `yaml:"tags,omitempty"`
|
||||||
Serial any `yaml:"serial,omitempty"` // int or string
|
Environment map[string]string `yaml:"environment,omitempty"`
|
||||||
MaxFailPercent int `yaml:"max_fail_percentage,omitempty"`
|
Serial any `yaml:"serial,omitempty"` // int or string
|
||||||
|
MaxFailPercent int `yaml:"max_fail_percentage,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnmarshalYAML handles play-level aliases such as ansible.builtin.import_playbook.
|
// UnmarshalYAML handles play-level aliases such as ansible.builtin.import_playbook.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue