fix(ansible): implement include_vars loading

This commit is contained in:
Virgil 2026-04-01 19:05:24 +00:00
parent 35b0cf03d9
commit 34229558fb
3 changed files with 161 additions and 9 deletions

View file

@ -150,19 +150,31 @@ func TestExecutorExtra_ModuleSetFact_Good_SkipsCacheable(t *testing.T) {
// --- moduleIncludeVars ---
func TestExecutorExtra_ModuleIncludeVars_Good_WithFile(t *testing.T) {
dir := t.TempDir()
path := joinPath(dir, "main.yml")
require.NoError(t, writeTestFile(path, []byte("app_name: demo\n"), 0644))
e := NewExecutor("/tmp")
result, err := e.moduleIncludeVars(map[string]any{"file": "vars/main.yml"})
result, err := e.moduleIncludeVars(map[string]any{"file": path})
require.NoError(t, err)
assert.Contains(t, result.Msg, "vars/main.yml")
assert.True(t, result.Changed)
assert.Contains(t, result.Msg, path)
assert.Equal(t, "demo", e.vars["app_name"])
}
func TestExecutorExtra_ModuleIncludeVars_Good_WithRawParams(t *testing.T) {
dir := t.TempDir()
path := joinPath(dir, "defaults.yml")
require.NoError(t, writeTestFile(path, []byte("app_port: 8080\n"), 0644))
e := NewExecutor("/tmp")
result, err := e.moduleIncludeVars(map[string]any{"_raw_params": "defaults.yml"})
result, err := e.moduleIncludeVars(map[string]any{"_raw_params": path})
require.NoError(t, err)
assert.Contains(t, result.Msg, "defaults.yml")
assert.True(t, result.Changed)
assert.Contains(t, result.Msg, path)
assert.Equal(t, 8080, e.vars["app_port"])
}
func TestExecutorExtra_ModuleIncludeVars_Good_Empty(t *testing.T) {

View file

@ -4,10 +4,14 @@ import (
"context"
"encoding/base64"
"io/fs"
"os"
"path/filepath"
"sort"
"strconv"
coreio "dappco.re/go/core/io"
coreerr "dappco.re/go/core/log"
"gopkg.in/yaml.v3"
)
// executeModule dispatches to the appropriate module handler.
@ -1261,14 +1265,99 @@ func (e *Executor) moduleIncludeVars(args map[string]any) (*TaskResult, error) {
if file == "" {
file = getStringArg(args, "_raw_params", "")
}
dir := getStringArg(args, "dir", "")
name := getStringArg(args, "name", "")
hashBehaviour := lower(getStringArg(args, "hash_behaviour", "replace"))
if file != "" {
// Would need to read and parse the vars file
// For now, just acknowledge
return &TaskResult{Changed: false, Msg: "include_vars: " + file}, nil
if file == "" && dir == "" {
return &TaskResult{Changed: false}, nil
}
return &TaskResult{Changed: false}, nil
loaded := make(map[string]any)
var sources []string
loadFile := func(path string) error {
data, err := coreio.Local.Read(path)
if err != nil {
return coreerr.E("Executor.moduleIncludeVars", "read vars file", err)
}
var vars map[string]any
if err := yaml.Unmarshal([]byte(data), &vars); err != nil {
return coreerr.E("Executor.moduleIncludeVars", "parse vars file", err)
}
mergeVars(loaded, vars, hashBehaviour == "merge")
return nil
}
if file != "" {
sources = append(sources, file)
if err := loadFile(file); err != nil {
return nil, err
}
}
if dir != "" {
entries, err := os.ReadDir(dir)
if err != nil {
return nil, coreerr.E("Executor.moduleIncludeVars", "read vars dir", err)
}
var files []string
for _, entry := range entries {
if entry.IsDir() {
continue
}
ext := lower(filepath.Ext(entry.Name()))
if ext == ".yml" || ext == ".yaml" {
files = append(files, joinPath(dir, entry.Name()))
}
}
sort.Strings(files)
for _, path := range files {
sources = append(sources, path)
if err := loadFile(path); err != nil {
return nil, err
}
}
}
if name != "" {
e.vars[name] = loaded
} else {
mergeVars(e.vars, loaded, hashBehaviour == "merge")
}
msg := "include_vars"
if len(sources) > 0 {
msg += ": " + join(", ", sources)
}
return &TaskResult{Changed: true, Msg: msg}, nil
}
func mergeVars(dst, src map[string]any, mergeMaps bool) {
if dst == nil || src == nil {
return
}
for key, val := range src {
if !mergeMaps {
dst[key] = val
continue
}
if existing, ok := dst[key].(map[string]any); ok {
if next, ok := val.(map[string]any); ok {
mergeVars(existing, next, true)
continue
}
}
dst[key] = val
}
}
func (e *Executor) moduleMeta(args map[string]any) (*TaskResult, error) {

View file

@ -702,6 +702,57 @@ func TestModulesAdv_ModuleUnarchive_Bad_LocalFileNotFound(t *testing.T) {
assert.Contains(t, err.Error(), "read src")
}
// --- include_vars module ---
func TestModulesAdv_ModuleIncludeVars_Good_LoadSingleFile(t *testing.T) {
dir := t.TempDir()
varsPath := joinPath(dir, "vars.yml")
require.NoError(t, writeTestFile(varsPath, []byte("app_name: demo\napp_port: 8080\nnested:\n enabled: true\n"), 0644))
e := NewExecutor("/tmp")
result, err := e.moduleIncludeVars(map[string]any{
"file": varsPath,
})
require.NoError(t, err)
assert.True(t, result.Changed)
assert.False(t, result.Failed)
assert.Contains(t, result.Msg, varsPath)
assert.Equal(t, "demo", e.vars["app_name"])
assert.Equal(t, 8080, e.vars["app_port"])
nested, ok := e.vars["nested"].(map[string]any)
require.True(t, ok)
assert.Equal(t, true, nested["enabled"])
}
func TestModulesAdv_ModuleIncludeVars_Good_LoadDirectoryWithMerge(t *testing.T) {
dir := t.TempDir()
require.NoError(t, writeTestFile(joinPath(dir, "01-base.yml"), []byte("app_name: demo\nnested:\n a: 1\n"), 0644))
require.NoError(t, writeTestFile(joinPath(dir, "02-override.yaml"), []byte("app_port: 8080\nnested:\n b: 2\n"), 0644))
e := NewExecutor("/tmp")
result, err := e.moduleIncludeVars(map[string]any{
"dir": dir,
"hash_behaviour": "merge",
})
require.NoError(t, err)
assert.True(t, result.Changed)
assert.False(t, result.Failed)
assert.Contains(t, result.Msg, joinPath(dir, "01-base.yml"))
assert.Contains(t, result.Msg, joinPath(dir, "02-override.yaml"))
assert.Equal(t, "demo", e.vars["app_name"])
assert.Equal(t, 8080, e.vars["app_port"])
nested, ok := e.vars["nested"].(map[string]any)
require.True(t, ok)
assert.Equal(t, 1, nested["a"])
assert.Equal(t, 2, nested["b"])
}
// --- uri module ---
func TestModulesAdv_ModuleURI_Good_GetRequestDefault(t *testing.T) {