From f5c47d58d8c82dcce02eb3414103cea1952f2b73 Mon Sep 17 00:00:00 2001 From: apenella Date: Sun, 18 Feb 2024 21:12:08 +0100 Subject: [PATCH] Import changes 1.3.0 (#137) import changes from v1.3.0 and adapt the AnsibleInventoryCmd to implement the Commander interface. --- CHANGELOG.md | 14 +- README.md | 20 + RELEASE_NOTES.md | 2 +- .../ansibleinventory-graph.go | 34 ++ examples/ansibleinventory-graph/inventory.yml | 22 + .../ansibleinventory-simple.go | 33 ++ .../ansibleinventory-simple/inventory.yml | 22 + .../ansible-inventory-vaulted-vars.go | 35 ++ .../inventory.yml | 16 + .../vault_password.cfg | 1 + go.mod | 8 +- go.sum | 16 +- pkg/inventory/inventory.go | 381 ++++++++++++++++++ pkg/inventory/inventory_test.go | 266 ++++++++++++ 14 files changed, 856 insertions(+), 14 deletions(-) create mode 100644 examples/ansibleinventory-graph/ansibleinventory-graph.go create mode 100644 examples/ansibleinventory-graph/inventory.yml create mode 100644 examples/ansibleinventory-simple/ansibleinventory-simple.go create mode 100644 examples/ansibleinventory-simple/inventory.yml create mode 100644 examples/ansibleinventory-vaulted-vars/ansible-inventory-vaulted-vars.go create mode 100644 examples/ansibleinventory-vaulted-vars/inventory.yml create mode 100644 examples/ansibleinventory-vaulted-vars/vault_password.cfg create mode 100644 pkg/inventory/inventory.go create mode 100644 pkg/inventory/inventory_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 45a5255..eb6ad2e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## v1.3.0 + +### Added + +- New feature to execute the Ansible inventory command. [#132](https://github.com/apenella/go-ansible/issues/132) + +## v1.2.2 + +### Changed + +- Bump golang.org/x/crypto from 0.8.0 to 0.17.0 + ## v1.2.1 -### Fix +### Fixed - In `AnsibleConnectionOptions`, add quotes to ssh, sftp, and scp arguments when generating the command diff --git a/README.md b/README.md index 93cac5b..2af88f5 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,9 @@ _**Important:** The master branch may contain unreleased or pre-released feature - [Stdoutcallback package](#stdoutcallback-package) - [ExecutorStdoutCallbackSetter interface](#executorstdoutcallbacksetter-interface) - [Stdout Callback Execute structs](#stdout-callback-execute-structs) + - [Inventory package](#inventory-package) + - [AnsibleInventoryCmd struct](#ansibleinventorycmd-struct) + - [AnsibleInventoryOptions struct](#ansibleinventoryoptions-struct) - [Options package](#options-package) - [AnsibleConnectionOptions struct](#ansibleconnectionoptions-struct) - [AnsiblePrivilegeEscalationOptions struct](#ansibleprivilegeescalationoptions-struct) @@ -644,6 +647,23 @@ if err != nil { } ``` +### Inventory package + +The information provided in this section gives an overview of the `Inventory` package in `go-ansible`. + +The `github.com/apenella/go-ansible/pkg/inventory` package provides the functionality to execute `ansible-inventory`. To perform these tasks, you can use the following inventory structs: + +#### AnsibleInventoryCmd struct + +The `AnsibleInventoryCmd` struct enables the generation of `ansible-inventory` commands. It implements the [Commander](#commander-interface) interface, so its method `Command` returns an array of strings that represents the command to be executed. An executor can use it to create the command to be executed. + +> Note +> Unlike other _Ansible_ commands, the `ansible-inventory` command does not provide privilege escalation or connection options, aligning with the functionality of the command itself. + +#### AnsibleInventoryOptions struct + +The `AnsibleInventoryOptions` struct includes parameters described in the `Options` section of the _Ansible_ manual page. It defines the behavior of the Ansible inventory operations and specifies where to find the configuration settings. + ### Options package These options can be used to customize the behaviour of `ansible` and `ansible-playbook` commands executions. diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index ce4c63d..eda75e6 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -31,7 +31,7 @@ Version 2.0.0 of *go-ansible* introduces several disruptive changes. Read the up - A new package `github.com/apenella/go-ansible/pkg/execute/stdoutcallback`. This package offers multiple decorators designed to set the stdout callback for Ansible executions. - An utility to generate the code for the configuration package has been introduced. This utility is located in the `utils/cmd/configGenerator.go`. -### Changed +### Added - The `AdhocPlaybookCmd` struct has been updated to implement the `Commander` interface. - The `AnsiblePlaybookCmd` struct has been updated to implement the `Commander` interface. diff --git a/examples/ansibleinventory-graph/ansibleinventory-graph.go b/examples/ansibleinventory-graph/ansibleinventory-graph.go new file mode 100644 index 0000000..bde08ec --- /dev/null +++ b/examples/ansibleinventory-graph/ansibleinventory-graph.go @@ -0,0 +1,34 @@ +package main + +import ( + "context" + "fmt" + + "github.com/apenella/go-ansible/pkg/execute" + "github.com/apenella/go-ansible/pkg/inventory" +) + +func main() { + ansibleInventoryOptions := inventory.AnsibleInventoryOptions{ + Graph: true, + Inventory: "inventory.yml", + Vars: true, + Yaml: true, + } + + inventoryCmd := &inventory.AnsibleInventoryCmd{ + Pattern: "all", + Options: &ansibleInventoryOptions, + } + + fmt.Println("Test strings:", inventoryCmd.String()) + + exec := execute.NewDefaultExecute( + execute.WithCmd(inventoryCmd), + ) + + err := exec.Execute(context.TODO()) + if err != nil { + panic(err) + } +} diff --git a/examples/ansibleinventory-graph/inventory.yml b/examples/ansibleinventory-graph/inventory.yml new file mode 100644 index 0000000..f796f73 --- /dev/null +++ b/examples/ansibleinventory-graph/inventory.yml @@ -0,0 +1,22 @@ +all: + children: + webserver: + hosts: + web1: + ansible_host: 192.168.1.101 + http_port: 80 + max_clients: 200 + web2: + ansible_host: 192.168.1.102 + http_port: 80 + max_clients: 150 + + database: + hosts: + db1: + ansible_host: 192.168.1.103 + db_port: 5432 + db_name: 'mydb' + + vars: + ansible_user: 'admin' diff --git a/examples/ansibleinventory-simple/ansibleinventory-simple.go b/examples/ansibleinventory-simple/ansibleinventory-simple.go new file mode 100644 index 0000000..87da98b --- /dev/null +++ b/examples/ansibleinventory-simple/ansibleinventory-simple.go @@ -0,0 +1,33 @@ +package main + +import ( + "context" + "fmt" + + "github.com/apenella/go-ansible/pkg/execute" + "github.com/apenella/go-ansible/pkg/inventory" +) + +func main() { + ansibleInventoryOptions := inventory.AnsibleInventoryOptions{ + Inventory: "inventory.yml", + List: true, + Yaml: true, + } + + inventoryCmd := &inventory.AnsibleInventoryCmd{ + Pattern: "all", + Options: &ansibleInventoryOptions, + } + + fmt.Println("Test strings:", inventoryCmd.String()) + + exec := execute.NewDefaultExecute( + execute.WithCmd(inventoryCmd), + ) + + err := exec.Execute(context.TODO()) + if err != nil { + panic(err) + } +} diff --git a/examples/ansibleinventory-simple/inventory.yml b/examples/ansibleinventory-simple/inventory.yml new file mode 100644 index 0000000..f796f73 --- /dev/null +++ b/examples/ansibleinventory-simple/inventory.yml @@ -0,0 +1,22 @@ +all: + children: + webserver: + hosts: + web1: + ansible_host: 192.168.1.101 + http_port: 80 + max_clients: 200 + web2: + ansible_host: 192.168.1.102 + http_port: 80 + max_clients: 150 + + database: + hosts: + db1: + ansible_host: 192.168.1.103 + db_port: 5432 + db_name: 'mydb' + + vars: + ansible_user: 'admin' diff --git a/examples/ansibleinventory-vaulted-vars/ansible-inventory-vaulted-vars.go b/examples/ansibleinventory-vaulted-vars/ansible-inventory-vaulted-vars.go new file mode 100644 index 0000000..22f317a --- /dev/null +++ b/examples/ansibleinventory-vaulted-vars/ansible-inventory-vaulted-vars.go @@ -0,0 +1,35 @@ +package main + +import ( + "context" + "fmt" + + "github.com/apenella/go-ansible/pkg/execute" + "github.com/apenella/go-ansible/pkg/inventory" +) + +func main() { + ansibleInventoryOptions := inventory.AnsibleInventoryOptions{ + Graph: true, + Inventory: "inventory.yml", + Vars: true, + Yaml: true, + VaultPasswordFile: "vault_password.cfg", + } + + inventoryCmd := &inventory.AnsibleInventoryCmd{ + Pattern: "all", + Options: &ansibleInventoryOptions, + } + + fmt.Println("Test strings:", inventoryCmd.String()) + + exec := execute.NewDefaultExecute( + execute.WithCmd(inventoryCmd), + ) + + err := exec.Execute(context.TODO()) + if err != nil { + panic(err) + } +} diff --git a/examples/ansibleinventory-vaulted-vars/inventory.yml b/examples/ansibleinventory-vaulted-vars/inventory.yml new file mode 100644 index 0000000..2808cde --- /dev/null +++ b/examples/ansibleinventory-vaulted-vars/inventory.yml @@ -0,0 +1,16 @@ +all: + children: + database_servers: + hosts: + db1.example.com: + db2.example.com: + vars: + ansible_ssh_user: dbadmin + ansible_ssh_pass: !vault | + $ANSIBLE_VAULT;1.1;AES256 + 35666433353234313161636238653633313264383230316233656236313935313465666533646164 + 3039353461343961616134653164666663646362646336360a383332663562396237613264386636 + 66663233616235396662383434343966333665383937653839326633333861366162616365353533 + 6237653031613664340a383736643130303935633164366536363561663334643763343661666138 + 62343234643536663061316666626466343265353065333439643065313134633132 + db_port: 5432 diff --git a/examples/ansibleinventory-vaulted-vars/vault_password.cfg b/examples/ansibleinventory-vaulted-vars/vault_password.cfg new file mode 100644 index 0000000..4970afc --- /dev/null +++ b/examples/ansibleinventory-vaulted-vars/vault_password.cfg @@ -0,0 +1 @@ +ThatIsAPassword \ No newline at end of file diff --git a/go.mod b/go.mod index e3adff3..780297c 100644 --- a/go.mod +++ b/go.mod @@ -26,10 +26,10 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/stretchr/objx v0.4.0 // indirect - golang.org/x/crypto v0.8.0 // indirect - golang.org/x/net v0.9.0 // indirect - golang.org/x/sys v0.7.0 // indirect - golang.org/x/text v0.9.0 // indirect + golang.org/x/crypto v0.19.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.17.0 // indirect + golang.org/x/text v0.14.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 8939975..d3869cf 100644 --- a/go.sum +++ b/go.sum @@ -197,8 +197,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ= -golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -268,8 +268,8 @@ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -331,8 +331,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -345,8 +345,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= diff --git a/pkg/inventory/inventory.go b/pkg/inventory/inventory.go new file mode 100644 index 0000000..932ff57 --- /dev/null +++ b/pkg/inventory/inventory.go @@ -0,0 +1,381 @@ +package inventory + +import ( + "fmt" + + errors "github.com/apenella/go-common-utils/error" +) + +const ( + // DefaultAnsibleInventoryBinary is the default value for ansible binary file to run inventory modules + DefaultAnsibleInventoryBinary = "ansible-inventory" + + // AskVaultPasswordFlag ask for vault password + AskVaultPasswordFlag = "--ask-vault-password" + + // ExportFlag When doing an –list, represent in a way that is optimized for export, not as an accurate representation of how Ansible has processed it + ExportFlag = "--export" + + // GraphFlag create inventory graph, if supplying pattern it must be a valid group name + GraphFlag = "--graph" + + // HostFlag Output specific host info, works as inventory script + HostFlag = "--host" + + // InventoryFlag is the inventory flag for ansible-inventory + InventoryFlag = "--inventory" + + // LimitFlag further limit selected hosts to an additional pattern + LimitFlag = "--limit" + + // ListFlag Output all hosts info, works as inventory script + ListFlag = "--list" + + // OutputFlag When doing –list, send the inventory to a file instead of to the screen + OutputFlag = "--output" + + // PlaybookDirFlag Since this tool does not use playbooks, use this as a substitute inventory directory. This sets the relative path for many features including roles/ group_vars/ etc. + PlaybookDirFlag = "--playbook-dir" + + // TomlFlag Use TOML format instead of default JSON, ignored for –graph + TomlFlag = "--toml" + + // VarsFlag Add vars to graph display, ignored unless used with –graph + VarsFlag = "--vars" + + // ValutIdFlag the vault identity to use + VaultIdFlag = "--vault-id" + + // ValutPasswordFileFlag vault password file + VaultPasswordFileFlag = "--vault-password-file" + + // VerboseFlag verbose with -vvvv is enabled + VerboseFlag = "-vvvv" + + // VerboseVFlag verbose with -v is enabled + VerboseVFlag = "-v" + + // VerboseVVFlag verbose with -vv is enabled + VerboseVVFlag = "-vv" + + // VerboseVVVFlag verbose with -vvv is enabled + VerboseVVVFlag = "-vvv" + + // VerboseVVVVFlag verbose with -vvvv is enabled + VerboseVVVVFlag = "-vvvv" + + // VersionFlag show program’s version number, config file location, configured module search path, module location, executable location and exit + VersionFlag = "--version" + + // YamlFlag Use YAML format instead of default JSON, ignored for –graph + YamlFlag = "--yaml" +) + +// AnsibleInventoryOptionFunc is a function to set executor options +type AnsibleInventoryOptionFunc func(*AnsibleInventoryCmd) + +// AnsibleInventoryCmd object is the main object which defines the `ansible-inventory` inventory command and how to execute it. +type AnsibleInventoryCmd struct { + // Ansible-inventory binary file + Binary string + // Pattern is the ansible's group pattern + Pattern string + // Options are the ansible's inventory options + Options *AnsibleInventoryOptions +} + +// Command generate the ansible command which will be executed +func (p *AnsibleInventoryCmd) Command() ([]string, error) { + cmd := []string{} + + // Use default binary when it is not already defined + if p.Binary == "" { + p.Binary = DefaultAnsibleInventoryBinary + } + + // Set the ansible-inventory binary file + cmd = append(cmd, p.Binary) + + // set the pattern + cmd = append(cmd, p.Pattern) + + // Determine the options to be set + if p.Options != nil { + options, err := p.Options.GenerateCommandOptions() + if err != nil { + return nil, errors.New("(inventory::Command)", "Error creating options", err) + } + cmd = append(cmd, options...) + } + + return cmd, nil +} + +// String returns AnsibleInventoryCmd as string +func (p *AnsibleInventoryCmd) String() string { + + // Use default binary when it is not already defined + if p.Binary == "" { + p.Binary = DefaultAnsibleInventoryBinary + } + + str := p.Binary + + str = fmt.Sprintf("%s %s", str, p.Pattern) + + if p.Options != nil { + str = fmt.Sprintf("%s %s", str, p.Options.String()) + } + + return str +} + +// AnsibleInventoryOptions object has those parameters described on `Options` section within ansible-inventory's man page, and which defines which should be the ansible-inventory execution behavior. +type AnsibleInventoryOptions struct { + // AskVaultPassword ask for vault password + AskVaultPassword bool + + // Export When doing an –list, represent in a way that is optimized for export,not as an accurate representation of how Ansible has processed it + Export bool + + // Graph create inventory graph, if supplying pattern it must be a valid group name + Graph bool + + // Host Output specific host info, works as inventory script + Host string + + // Inventory is the inventory flag for ansible-inventory + Inventory string + + // Limit further limit selected hosts to an additional pattern + Limit string + + // List Output all hosts info, works as inventory script + List bool + + // Output When doing –list, send the inventory to a file instead of to the screen + Output string + + // PlaybookDir Since this tool does not use playbooks, use this as a substitute inventory directory.This sets the relative path for many features including roles/ group_vars/ etc. + PlaybookDir string + + // Toml Use TOML format instead of default JSON, ignored for –graph + Toml bool + + // Vars Add vars to graph display, ignored unless used with –graph + Vars bool + + // VaultID the vault identity to use + VaultID string + + // VaultPasswordFile vault password file + VaultPasswordFile string + + // Verbose verbose mode enabled + Verbose bool + + // VerboseV verbose with -v is enabled + VerboseV bool + + // VerboseVV verbose with -vv is enabled + VerboseVV bool + + // VerboseVVV verbose with -vvv is enabled + VerboseVVV bool + + // VerboseVVVV verbose with -vvvv is enabled + VerboseVVVV bool + + // Version show program’s version number, config file location, configured module search path, module location, executable location and exit + Version bool + + // Yaml Use YAML format instead of default JSON, ignored for –graph + Yaml bool +} + +// GenerateCommandOptions return a list of command options flags to be used on ansible execution +func (o *AnsibleInventoryOptions) GenerateCommandOptions() ([]string, error) { + errContext := "(inventory::GenerateCommandOptions)" + + cmd := []string{} + + if o == nil { + return nil, errors.New(errContext, "AnsibleInventoryOptions is nil") + } + + if o.AskVaultPassword { + cmd = append(cmd, AskVaultPasswordFlag) + } + + if o.Export { + cmd = append(cmd, ExportFlag) + } + + if o.Graph { + cmd = append(cmd, GraphFlag) + } + + if o.Host != "" { + cmd = append(cmd, HostFlag) + cmd = append(cmd, o.Host) + } + + if o.Inventory != "" { + cmd = append(cmd, InventoryFlag) + cmd = append(cmd, o.Inventory) + } + + if o.Limit != "" { + cmd = append(cmd, LimitFlag) + cmd = append(cmd, o.Limit) + } + + if o.List { + cmd = append(cmd, ListFlag) + } + + if o.Output != "" { + cmd = append(cmd, OutputFlag) + cmd = append(cmd, o.Output) + } + + if o.PlaybookDir != "" { + cmd = append(cmd, PlaybookDirFlag) + cmd = append(cmd, o.PlaybookDir) + } + + if o.Toml { + cmd = append(cmd, TomlFlag) + } + + if o.Vars { + cmd = append(cmd, VarsFlag) + } + + if o.VaultID != "" { + cmd = append(cmd, VaultIdFlag) + cmd = append(cmd, o.VaultID) + } + + if o.VaultPasswordFile != "" { + cmd = append(cmd, VaultPasswordFileFlag) + cmd = append(cmd, o.VaultPasswordFile) + } + + if o.Version { + cmd = append(cmd, VersionFlag) + } + + if o.Verbose { + // Assuming there is a method to generate the correct verbosity flag + verboseFlag, err := o.generateVerbosityFlag() + if err != nil { + return nil, errors.New(errContext, "", err) + } + if verboseFlag != "" { + cmd = append(cmd, verboseFlag) + } + } + + if o.Yaml { + cmd = append(cmd, YamlFlag) + } + + return cmd, nil +} + +// generateVerbosityFlag return a string with the verbose flag. Higher verbosity (more v's) has precedence over lower +func (o *AnsibleInventoryOptions) generateVerbosityFlag() (string, error) { + if o.Verbose { + return VerboseFlag, nil + } + + if o.VerboseVVVV { + return VerboseVVVVFlag, nil + } + + if o.VerboseVVV { + return VerboseVVVFlag, nil + } + + if o.VerboseVV { + return VerboseVVFlag, nil + } + + if o.VerboseV { + return VerboseVFlag, nil + } + + return "", nil +} + +// GenerateCommandCommonOptions return a list of command options flags to be used on ansible execution +func (o *AnsibleInventoryOptions) String() string { + str := "" + + if o.AskVaultPassword { + str = fmt.Sprintf("%s %s", str, AskVaultPasswordFlag) + } + + if o.Export { + str = fmt.Sprintf("%s %s", str, ExportFlag) + } + + if o.Graph { + str = fmt.Sprintf("%s %s", str, GraphFlag) + } + + if o.Host != "" { + str = fmt.Sprintf("%s %s %s", str, HostFlag, o.Host) + } + + if o.Inventory != "" { + str = fmt.Sprintf("%s %s %s", str, InventoryFlag, o.Inventory) + } + + if o.Limit != "" { + str = fmt.Sprintf("%s %s %s", str, LimitFlag, o.Limit) + } + + if o.List { + str = fmt.Sprintf("%s %s", str, ListFlag) + } + + if o.Output != "" { + str = fmt.Sprintf("%s %s %s", str, OutputFlag, o.Output) + } + + if o.PlaybookDir != "" { + str = fmt.Sprintf("%s %s %s", str, PlaybookDirFlag, o.PlaybookDir) + } + + if o.Toml { + str = fmt.Sprintf("%s %s", str, TomlFlag) + } + + if o.Vars { + str = fmt.Sprintf("%s %s", str, VarsFlag) + } + + if o.VaultID != "" { + str = fmt.Sprintf("%s %s %s", str, VaultIdFlag, o.VaultID) + } + + if o.VaultPasswordFile != "" { + str = fmt.Sprintf("%s %s %s", str, VaultPasswordFileFlag, o.VaultPasswordFile) + } + + if o.Verbose { + str = fmt.Sprintf("%s %s", str, VerboseFlag) + } + + if o.Version { + str = fmt.Sprintf("%s %s", str, VersionFlag) + } + + if o.Yaml { + str = fmt.Sprintf("%s %s", str, YamlFlag) + } + + return str +} diff --git a/pkg/inventory/inventory_test.go b/pkg/inventory/inventory_test.go new file mode 100644 index 0000000..185b43f --- /dev/null +++ b/pkg/inventory/inventory_test.go @@ -0,0 +1,266 @@ +package inventory + +import ( + "testing" + + errors "github.com/apenella/go-common-utils/error" + "github.com/stretchr/testify/assert" +) + +// TestGenerateCommandOptions tests +func TestGenerateCommandOptions(t *testing.T) { + tests := []struct { + desc string + ansibleInventoryOptions *AnsibleInventoryOptions + err error + options []string + }{ + { + desc: "Testing nil AnsibleInventoryOptions definition", + ansibleInventoryOptions: nil, + err: errors.New("(inventory::GenerateCommandOptions)", "AnsibleInventoryOptions is nil"), + options: nil, + }, + { + desc: "Testing an empty AnsibleInventoryOptions definition", + ansibleInventoryOptions: &AnsibleInventoryOptions{}, + err: nil, + options: []string{}, + }, + { + desc: "Testing AnsibleInventoryOptions except vars", + ansibleInventoryOptions: &AnsibleInventoryOptions{ + Host: "localhost", + Inventory: "test/ansible/inventory/all", + Output: "/tmp/output.ini", + }, + err: nil, + options: []string{"--host", "localhost", "--inventory", "test/ansible/inventory/all", "--output", "/tmp/output.ini"}, + }, + { + desc: "Testing AnsibleInventoryOptions with vars", + ansibleInventoryOptions: &AnsibleInventoryOptions{ + Vars: true, + Host: "localhost", + Inventory: "test/ansible/inventory/all", + Output: "/tmp/output.ini", + }, + err: nil, + options: []string{"--host", "localhost", "--inventory", "test/ansible/inventory/all", "--output", "/tmp/output.ini", "--vars"}, + }, + } + + for _, test := range tests { + + t.Run(test.desc, func(t *testing.T) { + t.Log(test.desc) + + options, err := test.ansibleInventoryOptions.GenerateCommandOptions() + + if err != nil && assert.Error(t, err) { + assert.Equal(t, test.err, err) + } else { + assert.Equal(t, test.options, options, "Unexpected options value") + } + }) + } +} + +// TestString tests +func TestString(t *testing.T) { + tests := []struct { + desc string + err error + ansibleInventoryCmd *AnsibleInventoryCmd + res string + }{ + { + desc: "Testing AnsibleInventoryCmd to string", + err: nil, + ansibleInventoryCmd: &AnsibleInventoryCmd{ + Binary: "ansible-inventory", + Pattern: "all", + Options: &AnsibleInventoryOptions{ + AskVaultPassword: true, + Export: true, + Graph: true, + Host: "localhost", + Inventory: "test/ansible/inventory/all", + Limit: "myhost", + List: true, + Output: "/tmp/output.ini", + PlaybookDir: "/playbook/", + Toml: true, + Vars: true, + VaultID: "asdf", + VaultPasswordFile: "/vault/password/file", + Verbose: true, + Version: true, + Yaml: true, + }, + }, + res: "ansible-inventory all --ask-vault-password --export --graph --host localhost --inventory test/ansible/inventory/all --limit myhost --list --output /tmp/output.ini --playbook-dir /playbook/ --toml --vars --vault-id asdf --vault-password-file /vault/password/file -vvvv --version --yaml", + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + t.Log(test.desc) + + res := test.ansibleInventoryCmd.String() + + assert.Equal(t, test.res, res, "Unexpected value") + }) + } +} + +// TestCommand tests +func TestCommand(t *testing.T) { + tests := []struct { + desc string + err error + AnsibleInventoryCmd *AnsibleInventoryCmd + command []string + }{ + { + desc: "Testing generate AnsibleInventoryCmd command", + err: nil, + AnsibleInventoryCmd: &AnsibleInventoryCmd{ + Pattern: "all", + Options: &AnsibleInventoryOptions{ + AskVaultPassword: true, + Export: true, + Graph: true, + Host: "localhost", + Inventory: "test/ansible/inventory/all", + Limit: "limit", + List: true, + Output: "/tmp/output.ini", + PlaybookDir: "playbook-dir", + Toml: true, + Vars: true, + VaultID: "vault-id", + VaultPasswordFile: "vault-password-file", + Verbose: true, + Version: true, + Yaml: true, + }, + }, + command: []string{ + "ansible-inventory", + "all", + "--ask-vault-password", + "--export", + "--graph", + "--host", + "localhost", + "--inventory", + "test/ansible/inventory/all", + "--limit", + "limit", + "--list", + "--output", + "/tmp/output.ini", + "--playbook-dir", + "playbook-dir", + "--toml", + "--vars", + "--vault-id", + "vault-id", + "--vault-password-file", + "vault-password-file", + "--version", + "-vvvv", + "--yaml", + }, + }, + } + + for _, test := range tests { + + t.Run(test.desc, func(t *testing.T) { + t.Log(test.desc) + + command, err := test.AnsibleInventoryCmd.Command() + + if err != nil && assert.Error(t, err) { + assert.Equal(t, test.err, err) + } else { + assert.Equal(t, test.command, command, "Unexpected value") + } + }) + } +} + +// TestGenerateVerbosityFlag tests +func TestGenerateVerbosityFlag(t *testing.T) { + tests := []struct { + desc string + options *AnsibleInventoryOptions + res string + err error + }{ + { + desc: "Testing generate verbosity flag", + options: &AnsibleInventoryOptions{ + Verbose: true, + }, + res: "-vvvv", + err: &errors.Error{}, + }, + { + desc: "Testing generate verbosity flag V", + options: &AnsibleInventoryOptions{ + VerboseV: true, + }, + res: "-v", + err: &errors.Error{}, + }, + { + desc: "Testing generate verbosity flag VV", + options: &AnsibleInventoryOptions{ + VerboseVV: true, + }, + res: "-vv", + err: &errors.Error{}, + }, + { + desc: "Testing generate verbosity flag VVV", + options: &AnsibleInventoryOptions{ + VerboseVVV: true, + }, + res: "-vvv", + err: &errors.Error{}, + }, + { + desc: "Testing generate verbosity flag VVVV", + options: &AnsibleInventoryOptions{ + VerboseVVVV: true, + }, + res: "-vvvv", + err: &errors.Error{}, + }, + { + desc: "Testing generate verbosity flag VV has precedence over V", + options: &AnsibleInventoryOptions{ + VerboseVV: true, + VerboseV: true, + }, + res: "-vv", + err: &errors.Error{}, + }, + } + + for _, test := range tests { + t.Run(test.desc, func(t *testing.T) { + t.Log(test.desc) + + res, err := test.options.generateVerbosityFlag() + if err != nil && assert.Error(t, err) { + assert.Equal(t, test.err, err) + } else { + assert.Equal(t, test.res, res) + } + }) + } +}