feat(ansible): add virtual setup facts support
Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
parent
d34aed9feb
commit
56d532885d
5 changed files with 116 additions and 25 deletions
10
executor.go
10
executor.go
|
|
@ -2498,7 +2498,7 @@ func (e *Executor) gatherFacts(ctx context.Context, host string, play *Play) err
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
facts, err := e.collectFacts(ctx, client)
|
facts, err := e.collectFacts(ctx, client, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -2922,6 +2922,10 @@ func (e *Executor) lookupConditionValue(name string, host string, task *Task, lo
|
||||||
return facts.Architecture, true
|
return facts.Architecture, true
|
||||||
case "ansible_kernel":
|
case "ansible_kernel":
|
||||||
return facts.Kernel, true
|
return facts.Kernel, true
|
||||||
|
case "ansible_virtualization_role":
|
||||||
|
return facts.VirtualizationRole, true
|
||||||
|
case "ansible_virtualization_type":
|
||||||
|
return facts.VirtualizationType, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3215,6 +3219,10 @@ func (e *Executor) resolveExprBase(expr string, host string, task *Task) string
|
||||||
return facts.Architecture
|
return facts.Architecture
|
||||||
case "ansible_kernel":
|
case "ansible_kernel":
|
||||||
return facts.Kernel
|
return facts.Kernel
|
||||||
|
case "ansible_virtualization_role":
|
||||||
|
return facts.VirtualizationRole
|
||||||
|
case "ansible_virtualization_type":
|
||||||
|
return facts.VirtualizationType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
46
modules.go
46
modules.go
|
|
@ -3885,13 +3885,16 @@ func (e *Executor) moduleSetup(ctx context.Context, host string, client sshFacts
|
||||||
defer cancel()
|
defer cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
facts, err := e.collectFacts(ctx, client)
|
gatherSubset := normalizeStringList(args["gather_subset"])
|
||||||
|
includeVirtual := containsString(gatherSubset, "all") || containsString(gatherSubset, "virtual")
|
||||||
|
|
||||||
|
facts, err := e.collectFacts(ctx, client, includeVirtual)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &TaskResult{Failed: true, Msg: err.Error()}, nil
|
return &TaskResult{Failed: true, Msg: err.Error()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
factMap := factsToMap(facts)
|
factMap := factsToMap(facts)
|
||||||
factMap = applyGatherSubsetFilter(factMap, normalizeStringList(args["gather_subset"]))
|
factMap = applyGatherSubsetFilter(factMap, gatherSubset)
|
||||||
filteredFactMap := filterFactsMap(factMap, normalizeStringList(args["filter"]))
|
filteredFactMap := filterFactsMap(factMap, normalizeStringList(args["filter"]))
|
||||||
filteredFacts := factsFromMap(filteredFactMap)
|
filteredFacts := factsFromMap(filteredFactMap)
|
||||||
|
|
||||||
|
|
@ -3906,6 +3909,15 @@ func (e *Executor) moduleSetup(ctx context.Context, host string, client sshFacts
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func containsString(values []string, target string) bool {
|
||||||
|
for _, value := range values {
|
||||||
|
if lower(corexTrimSpace(value)) == target {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func applyGatherSubsetFilter(facts map[string]any, subsets []string) map[string]any {
|
func applyGatherSubsetFilter(facts map[string]any, subsets []string) map[string]any {
|
||||||
if len(facts) == 0 || len(subsets) == 0 {
|
if len(facts) == 0 || len(subsets) == 0 {
|
||||||
return facts
|
return facts
|
||||||
|
|
@ -4049,13 +4061,16 @@ func gatherSubsetKeys(subset string) []string {
|
||||||
"ansible_distribution_version",
|
"ansible_distribution_version",
|
||||||
}
|
}
|
||||||
case "virtual":
|
case "virtual":
|
||||||
return nil
|
return []string{
|
||||||
|
"ansible_virtualization_role",
|
||||||
|
"ansible_virtualization_type",
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Executor) collectFacts(ctx context.Context, client sshFactsRunner) (*Facts, error) {
|
func (e *Executor) collectFacts(ctx context.Context, client sshFactsRunner, includeVirtual bool) (*Facts, error) {
|
||||||
facts := &Facts{}
|
facts := &Facts{}
|
||||||
read := func(cmd string) (string, error) {
|
read := func(cmd string) (string, error) {
|
||||||
stdout, _, _, err := client.Run(ctx, cmd)
|
stdout, _, _, err := client.Run(ctx, cmd)
|
||||||
|
|
@ -4154,6 +4169,21 @@ func (e *Executor) collectFacts(ctx context.Context, client sshFactsRunner) (*Fa
|
||||||
facts.IPv4 = corexTrimSpace(stdout)
|
facts.IPv4 = corexTrimSpace(stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if includeVirtual {
|
||||||
|
stdout, err = read("systemd-detect-virt 2>/dev/null")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
virtType := corexTrimSpace(stdout)
|
||||||
|
if virtType == "" || virtType == "none" {
|
||||||
|
facts.VirtualizationRole = "host"
|
||||||
|
facts.VirtualizationType = "none"
|
||||||
|
} else {
|
||||||
|
facts.VirtualizationRole = "guest"
|
||||||
|
facts.VirtualizationType = virtType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return facts, nil
|
return facts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4170,6 +4200,8 @@ func factsToMap(facts *Facts) map[string]any {
|
||||||
"ansible_distribution_version": facts.Version,
|
"ansible_distribution_version": facts.Version,
|
||||||
"ansible_architecture": facts.Architecture,
|
"ansible_architecture": facts.Architecture,
|
||||||
"ansible_kernel": facts.Kernel,
|
"ansible_kernel": facts.Kernel,
|
||||||
|
"ansible_virtualization_role": facts.VirtualizationRole,
|
||||||
|
"ansible_virtualization_type": facts.VirtualizationType,
|
||||||
"ansible_memtotal_mb": facts.Memory,
|
"ansible_memtotal_mb": facts.Memory,
|
||||||
"ansible_processor_vcpus": facts.CPUs,
|
"ansible_processor_vcpus": facts.CPUs,
|
||||||
"ansible_default_ipv4_address": facts.IPv4,
|
"ansible_default_ipv4_address": facts.IPv4,
|
||||||
|
|
@ -4225,6 +4257,12 @@ func factsFromMap(values map[string]any) *Facts {
|
||||||
if v, ok := values["ansible_kernel"].(string); ok {
|
if v, ok := values["ansible_kernel"].(string); ok {
|
||||||
facts.Kernel = v
|
facts.Kernel = v
|
||||||
}
|
}
|
||||||
|
if v, ok := values["ansible_virtualization_role"].(string); ok {
|
||||||
|
facts.VirtualizationRole = v
|
||||||
|
}
|
||||||
|
if v, ok := values["ansible_virtualization_type"].(string); ok {
|
||||||
|
facts.VirtualizationType = v
|
||||||
|
}
|
||||||
if v, ok := values["ansible_memtotal_mb"].(int64); ok {
|
if v, ok := values["ansible_memtotal_mb"].(int64); ok {
|
||||||
facts.Memory = v
|
facts.Memory = v
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1071,6 +1071,45 @@ func TestModulesInfra_ModuleSetup_Good_FilteredFacts(t *testing.T) {
|
||||||
assert.Equal(t, "debian", e.facts["host1"].Distribution)
|
assert.Equal(t, "debian", e.facts["host1"].Distribution)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestModulesInfra_ModuleSetup_Good_VirtualSubset(t *testing.T) {
|
||||||
|
e, mock := newTestExecutorWithMock("host1")
|
||||||
|
|
||||||
|
mock.expectCommand(`hostname -f`, "web1.example.com\n", "", 0)
|
||||||
|
mock.expectCommand(`hostname -s`, "web1\n", "", 0)
|
||||||
|
mock.expectCommand(`cat /etc/os-release`, "ID=debian\nVERSION_ID=12\n", "", 0)
|
||||||
|
mock.expectCommand(`uname -m`, "x86_64\n", "", 0)
|
||||||
|
mock.expectCommand(`uname -r`, "6.1.0\n", "", 0)
|
||||||
|
mock.expectCommand(`nproc`, "8\n", "", 0)
|
||||||
|
mock.expectCommand(`free -m`, "16384\n", "", 0)
|
||||||
|
mock.expectCommand(`hostname -I`, "10.0.0.11\n", "", 0)
|
||||||
|
mock.expectCommand(`systemd-detect-virt`, "docker\n", "", 0)
|
||||||
|
|
||||||
|
task := &Task{
|
||||||
|
Module: "setup",
|
||||||
|
Args: map[string]any{
|
||||||
|
"gather_subset": "!all,!min,virtual",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
result, err := executeModuleWithMock(e, mock, "host1", task)
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotNil(t, result)
|
||||||
|
assert.False(t, result.Changed)
|
||||||
|
|
||||||
|
facts, ok := result.Data["ansible_facts"].(map[string]any)
|
||||||
|
require.True(t, ok)
|
||||||
|
assert.Len(t, facts, 2)
|
||||||
|
assert.Equal(t, "guest", facts["ansible_virtualization_role"])
|
||||||
|
assert.Equal(t, "docker", facts["ansible_virtualization_type"])
|
||||||
|
assert.NotContains(t, facts, "ansible_hostname")
|
||||||
|
|
||||||
|
require.NotNil(t, e.facts["host1"])
|
||||||
|
assert.Equal(t, "guest", e.facts["host1"].VirtualizationRole)
|
||||||
|
assert.Equal(t, "docker", e.facts["host1"].VirtualizationType)
|
||||||
|
assert.Equal(t, "guest", e.templateString("{{ ansible_virtualization_role }}", "host1", nil))
|
||||||
|
assert.Equal(t, "docker", e.templateString("{{ ansible_virtualization_type }}", "host1", nil))
|
||||||
|
}
|
||||||
|
|
||||||
func TestModulesInfra_ModuleSetup_Good_GatherSubset(t *testing.T) {
|
func TestModulesInfra_ModuleSetup_Good_GatherSubset(t *testing.T) {
|
||||||
e, mock := newTestExecutorWithMock("host1")
|
e, mock := newTestExecutorWithMock("host1")
|
||||||
|
|
||||||
|
|
|
||||||
2
types.go
2
types.go
|
|
@ -376,6 +376,8 @@ type Facts struct {
|
||||||
Version string `json:"ansible_distribution_version"`
|
Version string `json:"ansible_distribution_version"`
|
||||||
Architecture string `json:"ansible_architecture"`
|
Architecture string `json:"ansible_architecture"`
|
||||||
Kernel string `json:"ansible_kernel"`
|
Kernel string `json:"ansible_kernel"`
|
||||||
|
VirtualizationRole string `json:"ansible_virtualization_role"`
|
||||||
|
VirtualizationType string `json:"ansible_virtualization_type"`
|
||||||
Memory int64 `json:"ansible_memtotal_mb"`
|
Memory int64 `json:"ansible_memtotal_mb"`
|
||||||
CPUs int `json:"ansible_processor_vcpus"`
|
CPUs int `json:"ansible_processor_vcpus"`
|
||||||
IPv4 string `json:"ansible_default_ipv4_address"`
|
IPv4 string `json:"ansible_default_ipv4_address"`
|
||||||
|
|
|
||||||
|
|
@ -918,6 +918,8 @@ func TestTypes_Facts_Good_Struct(t *testing.T) {
|
||||||
Version: "24.04",
|
Version: "24.04",
|
||||||
Architecture: "x86_64",
|
Architecture: "x86_64",
|
||||||
Kernel: "6.8.0",
|
Kernel: "6.8.0",
|
||||||
|
VirtualizationRole: "guest",
|
||||||
|
VirtualizationType: "docker",
|
||||||
Memory: 16384,
|
Memory: 16384,
|
||||||
CPUs: 4,
|
CPUs: 4,
|
||||||
IPv4: "10.0.0.1",
|
IPv4: "10.0.0.1",
|
||||||
|
|
@ -927,6 +929,8 @@ func TestTypes_Facts_Good_Struct(t *testing.T) {
|
||||||
assert.Equal(t, "web1.example.com", facts.FQDN)
|
assert.Equal(t, "web1.example.com", facts.FQDN)
|
||||||
assert.Equal(t, "ubuntu", facts.Distribution)
|
assert.Equal(t, "ubuntu", facts.Distribution)
|
||||||
assert.Equal(t, "x86_64", facts.Architecture)
|
assert.Equal(t, "x86_64", facts.Architecture)
|
||||||
|
assert.Equal(t, "guest", facts.VirtualizationRole)
|
||||||
|
assert.Equal(t, "docker", facts.VirtualizationType)
|
||||||
assert.Equal(t, int64(16384), facts.Memory)
|
assert.Equal(t, int64(16384), facts.Memory)
|
||||||
assert.Equal(t, 4, facts.CPUs)
|
assert.Equal(t, 4, facts.CPUs)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue