diff --git a/executor.go b/executor.go index 286c348..ccf85c7 100644 --- a/executor.go +++ b/executor.go @@ -2342,6 +2342,12 @@ func (e *Executor) lookupConditionValue(name string, host string, task *Task, lo } } + if name == "ansible_facts" { + if facts, ok := e.facts[host]; ok { + return factsToMap(facts), true + } + } + if facts, ok := e.facts[host]; ok { switch name { case "ansible_hostname": @@ -2380,6 +2386,14 @@ func (e *Executor) lookupConditionValue(name string, host string, task *Task, lo } } + if base == "ansible_facts" { + if facts, ok := e.facts[host]; ok { + if nested, ok := lookupNestedValue(factsToMap(facts), path); ok { + return nested, true + } + } + } + if val, ok := e.vars[base]; ok { if nested, ok := lookupNestedValue(val, path); ok { return nested, true @@ -2580,6 +2594,9 @@ func (e *Executor) resolveExpr(expr string, host string, task *Task) string { // Check facts if facts, ok := e.facts[host]; ok { + if expr == "ansible_facts" { + return sprintf("%v", factsToMap(facts)) + } switch expr { case "ansible_hostname": return facts.Hostname @@ -2673,6 +2690,26 @@ func (e *Executor) lookupExprValue(name string, host string, task *Task) (any, b return val, true } } + + if name == "ansible_facts" { + if facts, ok := e.facts[host]; ok { + return factsToMap(facts), true + } + } + + if contains(name, ".") { + parts := splitN(name, ".", 2) + base := parts[0] + path := parts[1] + + if base == "ansible_facts" { + if facts, ok := e.facts[host]; ok { + if nested, ok := lookupNestedValue(factsToMap(facts), path); ok { + return nested, true + } + } + } + } return nil, false } @@ -3251,6 +3288,7 @@ func (e *Executor) TemplateFile(src, host string, task *Task) (string, error) { } // Add facts if facts, ok := e.facts[host]; ok { + context["ansible_facts"] = factsToMap(facts) context["ansible_hostname"] = facts.Hostname context["ansible_fqdn"] = facts.FQDN context["ansible_os_family"] = facts.OS diff --git a/modules_file_test.go b/modules_file_test.go index d3fd1f6..2825227 100644 --- a/modules_file_test.go +++ b/modules_file_test.go @@ -881,6 +881,30 @@ func TestModulesFile_ModuleTemplate_Good_BasicTemplate(t *testing.T) { assert.Contains(t, string(up.Content), "web01.example.com") } +func TestModulesFile_ModuleTemplate_Good_AnsibleFactsMapTemplate(t *testing.T) { + tmpDir := t.TempDir() + srcPath := joinPath(tmpDir, "facts.conf.j2") + require.NoError(t, writeTestFile(srcPath, []byte("host={{ ansible_facts.ansible_hostname }}"), 0644)) + + e, mock := newTestExecutorWithMock("host1") + e.facts["host1"] = &Facts{ + Hostname: "web01", + Distribution: "debian", + } + + result, err := moduleTemplateWithClient(e, mock, map[string]any{ + "src": srcPath, + "dest": "/etc/app/facts.conf", + }, "host1", &Task{}) + + require.NoError(t, err) + assert.True(t, result.Changed) + + up := mock.lastUpload() + require.NotNil(t, up) + assert.Contains(t, string(up.Content), "host=web01") +} + func TestModulesFile_ModuleTemplate_Good_CustomMode(t *testing.T) { tmpDir := t.TempDir() srcPath := joinPath(tmpDir, "script.sh.j2") diff --git a/modules_infra_test.go b/modules_infra_test.go index f574f91..8c8e702 100644 --- a/modules_infra_test.go +++ b/modules_infra_test.go @@ -970,6 +970,21 @@ func TestModulesInfra_Facts_Good_LocalhostFacts(t *testing.T) { assert.Equal(t, "localhost", result) } +func TestModulesInfra_Facts_Good_AnsibleFactsMapResolution(t *testing.T) { + e := NewExecutor("/tmp") + e.facts["host1"] = &Facts{ + Hostname: "web1", + FQDN: "web1.example.com", + Distribution: "debian", + Version: "12", + } + + assert.Equal(t, "web1", e.templateString("{{ ansible_facts.ansible_hostname }}", "host1", nil)) + assert.Equal(t, "debian", e.templateString("{{ ansible_facts.ansible_distribution }}", "host1", nil)) + assert.True(t, e.evalCondition("ansible_facts.ansible_hostname == 'web1'", "host1")) + assert.True(t, e.evalCondition("ansible_facts.ansible_distribution == 'debian'", "host1")) +} + func TestModulesInfra_ModuleSetup_Good_GathersAndStoresFacts(t *testing.T) { e, mock := newTestExecutorWithMock("host1")