-
-
Notifications
You must be signed in to change notification settings - Fork 134
Add Native Support for Ansible Components #1448
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Warning This PR exceeds the recommended limit of 1,000 lines.Large PRs are difficult to review and may be rejected due to their size. Please verify that this PR does not address multiple issues. |
Important Cloud Posse Engineering Team Review RequiredThis pull request modifies files that require Cloud Posse's review. Please be patient, and a core maintainer will review your changes. To expedite this process, reach out to us on Slack in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
golangci-lint found more than 20 potential problems in the proposed changes. Check the Files changed tab for more details.
For the Golang lint failures, should I suppress ( |
Add schema definition and implementation for Ansible components: - Add ansible section to atmos-manifest.json schema - Update demo-ansible example with proper schema reference - Modify list_components.go to support extracting ansible components - Refactor code to improve component type extraction
Is there also a good way to view the website to ensure it looks good locally? |
Replace strict component type validation with a more tolerant approach that doesn't error when specific component types are missing. This allows stacks to have any combination of component types without causing parsing errors. - Remove specific error types for each component type - Initialize empty maps and only populate when components exist - Update comment to reflect that function handles all component types
Yep! Try this and let us know if you run into problems.
|
Nono, those are legit. Taking a look at one of those, I can see |
@coderabbitai review |
✅ Actions performedReview triggered.
|
📝 WalkthroughWalkthroughAdds first-class Ansible support to Atmos: new CLI group and subcommands (playbook, inventory, vault, version), internal executor and utils, config/env/schema plumbing, stack processing integration, path/describe-affected updates, error types, list-components awareness, extensive docs, and a full demo under examples. Public APIs updated where stack processing signatures gained Ansible base-path parameters. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor User
participant CLI as Cobra (ansible/*)
participant Core as ansibleRun
participant Cfg as Config/Stacks Resolver
participant Exec as ExecuteAnsible
participant OS as System Shell
User->>CLI: atmos ansible playbook <component> -s <stack> [flags] -- [ansible-opts]
CLI->>Core: RunE(cmd, subcmd, args)
Core->>Cfg: getConfigAndStacksInfo("ansible", ...)
Cfg-->>Core: ConfigAndStacksInfo
Core->>Exec: ExecuteAnsible(info, flags)
Exec->>Exec: Validate stack/component/permissions
Exec->>Exec: Resolve playbook/inventory (flags > settings)
Exec->>Exec: Write vars file (if needed)
Exec->>Exec: Build args/env and working dir
Exec->>OS: Run ansible[-playbook|-inventory|-vault] ...
OS-->>Exec: Exit code/stdout/stderr
Exec->>Exec: Cleanup vars file (on success/fail as applicable)
Exec-->>Core: error/nil
Core-->>CLI: error/nil
CLI-->>User: Output/exit code
sequenceDiagram
autonumber
actor User
participant CLI as Cobra (ansible version)
participant Core as ansibleRun
participant Cfg as Config Resolver
participant Exec as ExecuteAnsible
participant OS as System Shell
User->>CLI: atmos ansible version
CLI->>Core: RunE(cmd,"version",[])
Core->>Cfg: getConfigAndStacksInfo(...)
Cfg-->>Core: info(SubCommand="version")
Core->>Exec: ExecuteAnsible(info)
Exec->>OS: Run ansible --version
OS-->>Exec: Version output
Exec-->>Core: nil
Core-->>User: Version output
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Possibly related PRs
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 23
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (5)
pkg/datafetcher/schema/config/global/1.0.json (1)
3-5
: Update Global Config schema $id and title
In pkg/datafetcher/schema/config/global/1.0.json (lines 3–4), replace the manifest schema identifiers to avoid resolver collisions:-"$id": "https://json.schemastore.org/atmos-manifest.json", -"title": "JSON Schema for Atmos Stack Manifest files. Version 1.0. https://atmos.tools", +"$id": "https://atmos.tools/schemas/config/global/1.0.json", +"title": "JSON Schema for Atmos Global Config files. Version 1.0. https://atmos.tools",pkg/datafetcher/schema/stacks/stack-config/1.0.json (1)
3-5
: $id/title reference the manifest schema, not the stack-config schema.Rename to the proper Stack Config identity to prevent ambiguity.
-"$id": "https://json.schemastore.org/atmos-manifest.json", -"title": "JSON Schema for Atmos Stack Manifest files. Version 1.0. https://atmos.tools", +"$id": "https://atmos.tools/schemas/stacks/stack-config/1.0.json", +"title": "JSON Schema for Atmos Stack Config files. Version 1.0. https://atmos.tools",internal/exec/describe_affected_utils.go (1)
667-876
: Ansible components are not considered in affected detection
findAffected
handles Terraform/Helmfile/Packer, but there’s no analogous block forcfg.AnsibleComponentType
. That means Ansible changes won’t surface indescribe affected
. Add a section mirroring Packer/Helmfile (metadata/env/vars/settings checks + component folder diff).Here’s a minimal insertion (just after the Packer section):
+ // Ansible + if ansibleSection, ok := componentsSection[cfg.AnsibleComponentType].(map[string]any); ok { + for componentName, compSection := range ansibleSection { + if componentSection, ok := compSection.(map[string]any); ok { + if metadataSection, ok := componentSection["metadata"].(map[string]any); ok { + if metadataType, ok := metadataSection["type"].(string); ok && metadataType == "abstract" { continue } + if !isComponentEnabled(metadataSection, componentName) { continue } + if excludeLocked && isComponentLocked(metadataSection) { continue } + if !isEqual(remoteStacks, stackName, cfg.AnsibleComponentType, componentName, metadataSection, "metadata") { + affected := schema.Affected{ ComponentType: cfg.AnsibleComponentType, Component: componentName, Stack: stackName, Affected: "stack.metadata" } + if err = appendToAffected(atmosConfig, componentName, stackName, &componentSection, &res, &affected, false, nil, includeSettings); err != nil { return nil, err } + } + } + if component, ok := componentSection[cfg.ComponentSectionName].(string); ok && component != "" { + changed, err := isComponentFolderChanged(component, cfg.AnsibleComponentType, atmosConfig, changedFiles) + if err != nil { return nil, err } + if changed { + affected := schema.Affected{ ComponentType: cfg.AnsibleComponentType, Component: componentName, Stack: stackName, Affected: "component" } + if err = appendToAffected(atmosConfig, componentName, stackName, &componentSection, &res, &affected, false, nil, includeSettings); err != nil { return nil, err } + } + } + if varSection, ok := componentSection["vars"].(map[string]any); ok { + if !isEqual(remoteStacks, stackName, cfg.AnsibleComponentType, componentName, varSection, "vars") { + affected := schema.Affected{ ComponentType: cfg.AnsibleComponentType, Component: componentName, Stack: stackName, Affected: "stack.vars" } + if err = appendToAffected(atmosConfig, componentName, stackName, &componentSection, &res, &affected, false, nil, includeSettings); err != nil { return nil, err } + } + } + if envSection, ok := componentSection["env"].(map[string]any); ok { + if !isEqual(remoteStacks, stackName, cfg.AnsibleComponentType, componentName, envSection, "env") { + affected := schema.Affected{ ComponentType: cfg.AnsibleComponentType, Component: componentName, Stack: stackName, Affected: "stack.env" } + if err = appendToAffected(atmosConfig, componentName, stackName, &componentSection, &res, &affected, false, nil, includeSettings); err != nil { return nil, err } + } + } + if settingsSection, ok := componentSection[cfg.SettingsSectionName].(map[string]any); ok { + if !isEqual(remoteStacks, stackName, cfg.AnsibleComponentType, componentName, settingsSection, cfg.SettingsSectionName) { + affected := schema.Affected{ ComponentType: cfg.AnsibleComponentType, Component: componentName, Stack: stackName, Affected: "stack.settings" } + if err = appendToAffected(atmosConfig, componentName, stackName, &componentSection, &res, &affected, false, nil, includeSettings); err != nil { return nil, err } + } + var stackComponentSettings schema.Settings + if err = mapstructure.Decode(settingsSection, &stackComponentSettings); err != nil { return nil, err } + if !reflect.ValueOf(stackComponentSettings).IsZero() && !reflect.ValueOf(stackComponentSettings.DependsOn).IsZero() { + isChanged, changedType, changedFoF, err := isComponentDependentFolderOrFileChanged(changedFiles, stackComponentSettings.DependsOn) + if err != nil { return nil, err } + if isChanged { + affected := schema.Affected{ + ComponentType: cfg.AnsibleComponentType, Component: componentName, Stack: stackName, Affected: changedType, + File: func() string { if changedType == "file" { return changedFoF } ; return "" }(), + Folder: func() string { if changedType == "folder" { return changedFoF } ; return "" }(), + } + if err = appendToAffected(atmosConfig, componentName, stackName, &componentSection, &res, &affected, false, nil, includeSettings); err != nil { return nil, err } + } + } + } + } + } + }Also ensure
isComponentFolderChanged
supportscfg.AnsibleComponentType
by resolvingatmosConfig.AnsibleDirAbsolutePath
.I can open a follow-up PR adding tests proving Ansible components are detected as affected.
pkg/list/list_components.go (1)
23-62
: Generalize: don’t hardcode component typesHardcoding terraform/helmfile/packer/ansible will require code changes for every new type. Iterate all entries under "components" and collect map keys.
Apply this refactor in-place:
- // Initialize empty maps for each component type - terraformComponents := make(map[string]any) - helmfileComponents := make(map[string]any) - packerComponents := make(map[string]any) - ansibleComponents := make(map[string]any) - - // Only try to get components if they exist, no error if they don't - if comp, ok := componentsMap["terraform"].(map[string]any); ok { - terraformComponents = comp - } - - if comp, ok := componentsMap["helmfile"].(map[string]any); ok { - helmfileComponents = comp - } - - if comp, ok := componentsMap["packer"].(map[string]any); ok { - packerComponents = comp - } - - if comp, ok := componentsMap["ansible"].(map[string]any); ok { - ansibleComponents = comp - } - - // Merge all component maps into one. - allComponents := lo.Assign(terraformComponents, helmfileComponents, packerComponents, ansibleComponents) - - return lo.Keys(allComponents), nil + // Aggregate keys from all known/unknown component groups + keys := make([]string, 0, 16) + for _, v := range componentsMap { + if m, ok := v.(map[string]any); ok { + keys = append(keys, lo.Keys(m)...) + } + } + return lo.Uniq(keys), nilinternal/exec/stack_processor_utils_test.go (1)
630-637
: Include the new Ansible component path and environment args in all ProcessStackConfig calls
- internal/exec/validate_stacks.go (around line 177): current invocation only passes
atmosConfig
andStacksBaseAbsolutePath
; it must also include the Terraform, Helmfile, Packer and Ansible component paths plus the environment string.- pkg/stack/stack_processor.go (around line 99): wrapper for
exec.ProcessStackConfig
needs to forward the additional arguments.- internal/exec/stack_processor_utils.go (around line 108): nested call to
ProcessStackConfig
must be updated to match the new signature.
🧹 Nitpick comments (72)
pkg/utils/type_utils.go (1)
5-6
: Tighten the comment (“non-empty” → “non-zero” for comparable T).Since T is comparable and this wraps lo.Coalesce, it returns the first non-zero value; if none, zero value. Consider updating the doc to reflect that.
-// Coalesce returns the first non-empty argument. Arguments must be comparable +// Coalesce returns the first non-zero argument for T (or the zero value if none). T must be comparable.examples/demo-ansible/components/ansible/webapp/README.md (4)
3-9
: Normalize NGINX casing for consistency.Use a single casing throughout (recommend “NGINX”).
-This is a demo Ansible component that configures a simple web application using nginx. +This is a demo Ansible component that configures a simple web application using NGINX. @@ -- `templates/app.conf.j2` - Nginx configuration template +- `templates/app.conf.j2` - NGINX configuration template
8-8
: Clarify inventory group naming.If
webservers
is a literal group, format it as code to avoid confusion with “web servers.”-- `inventory.yml` - Default inventory with webservers and load balancers +- `inventory.yml` - Default inventory with `webservers` and load balancers
15-20
: Document defaults and sources for vars.Please state where
environment
derives its value (e.g., from the Atmos stack) and confirm the default fortarget_hosts
is indeedwebservers
if not overridden.
11-21
: Add a short “Prerequisites” section.Suggest listing required tools (Atmos, Ansible), and any expected inventory/plugin assumptions. Keeps newcomers on the happy path.
Example to add after “Configuration”:
- Atmos installed and configured
- Ansible installed (version X+)
- SSH access to target hosts
cmd/markdown/atmos_ansible_version_usage.md (1)
1-5
: Fix markdownlint nits (code fence language, prompt symbol) and minor grammar.This addresses MD040/MD014 and reads a bit clearer.
Apply:
-– Show the version of ansible command +Show the version of the Ansible command - -``` -$ atmos ansible version -``` +```sh +atmos ansible version +```Also add a trailing newline at EOF.
cmd/markdown/atmos_ansible_inventory_usage.md (1)
1-11
: Normalize headings and fix markdownlint issues (language + prompt).Keeps style consistent and satisfies MD040/MD014.
Apply:
-– Display the configured inventory +Display the configured inventory - -``` -$ atmos ansible inventory <component-name> -s <stack-name> --list -``` +```sh +atmos ansible inventory <component-name> -s <stack-name> --list +``` - -– Display inventory for specific host +Display inventory for a specific host - -``` -$ atmos ansible inventory <component-name> -s <stack-name> --host <hostname> -``` +```sh +atmos ansible inventory <component-name> -s <stack-name> --host <hostname> +```cmd/markdown/atmos_ansible_vault_usage.md (1)
1-17
: Add code fence languages and drop prompt symbol to satisfy markdownlint; keep headings concise.Resolves MD040/MD014 across all blocks.
Apply:
-– Encrypt and decrypt files with Ansible Vault +Encrypt and decrypt files with Ansible Vault - -``` -$ atmos ansible vault <component-name> -s <stack-name> encrypt <file> -``` +```sh +atmos ansible vault <component-name> -s <stack-name> encrypt <file> +``` - -– Decrypt a vault file +Decrypt a vault file - -``` -$ atmos ansible vault <component-name> -s <stack-name> decrypt <file> -``` +```sh +atmos ansible vault <component-name> -s <stack-name> decrypt <file> +``` - -– Create a new encrypted file +Create a new encrypted file - -``` -$ atmos ansible vault <component-name> -s <stack-name> create <file> -``` +```sh +atmos ansible vault <component-name> -s <stack-name> create <file> +```cmd/markdown/atmos_ansible_usage.md (1)
1-13
: Fix markdownlint and style (add language, drop prompts, capitalize Ansible).Add code-fence languages and remove
$
to satisfy MD040/MD014. Also capitalize “Ansible.”-– Execute ansible commands for configuration management +Execute Ansible commands for configuration management -``` -$ atmos ansible playbook <component-name> -s <stack-name> -``` +```bash +atmos ansible playbook <component-name> -s <stack-name> +``` -``` -$ atmos ansible inventory <component-name> -s <stack-name> --list -``` +```bash +atmos ansible inventory <component-name> -s <stack-name> --list +``` -``` -$ atmos ansible vault <component-name> -s <stack-name> encrypt <file> -``` +```bash +atmos ansible vault <component-name> -s <stack-name> encrypt <file> +```examples/demo-ansible/components/ansible/webapp/ansible.cfg (2)
2-2
: Host key checking disabled — call out as demo-only.Leaving this False is risky in real environments. Add a warning comment or flip to True for non-demo use.
[defaults] - host_key_checking = False + # WARNING: Demo setting. Set to True in production to verify SSH host keys. + host_key_checking = False
13-15
: Use hashed ControlPath to avoid “too long” UNIX socket paths.Switch to
%%C
to hash the ControlPath (safer on long paths).[ssh_connection] ssh_args = -o ControlMaster=auto -o ControlPersist=60s pipelining = True -control_path = ~/.ansible/cp/ansible-ssh-%%h-%%p-%%r +control_path = ~/.ansible/cp/%%Ccmd/markdown/atmos_ansible_playbook_usage.md (1)
1-17
: Fix markdownlint and style (add language, drop prompts, normalize headings).Add
bash
fence language and remove$
to satisfy MD040/MD014. Normalize headings.-– Execute an Ansible playbook +Execute an Ansible playbook -``` -$ atmos ansible playbook <component-name> -s <stack-name> -``` +```bash +atmos ansible playbook <component-name> -s <stack-name> +``` -– Execute with custom playbook +Execute with custom playbook -``` -$ atmos ansible playbook <component-name> -s <stack-name> --playbook custom.yml -``` +```bash +atmos ansible playbook <component-name> -s <stack-name> --playbook custom.yml +``` -– Execute with custom inventory +Execute with custom inventory -``` -$ atmos ansible playbook <component-name> -s <stack-name> --inventory inventory.yml -``` +```bash +atmos ansible playbook <component-name> -s <stack-name> --inventory inventory.yml +```pkg/config/utils.go (1)
348-359
: Nice: ENV overrides for Ansible; add CLI flag parity + viper binding.
- Parity: Terraform/Helmfile/Packer have CLI override handlers; add analogous Ansible handling (e.g., setAnsibleConfig) and wire it into processCommandLineArgs.
- Binding: ensure ATMOS_COMPONENTS_ANSIBLE_COMMAND and ATMOS_COMPONENTS_ANSIBLE_BASE_PATH are bound via the selective setEnv/bindEnv pattern (per repo convention), not just read via os.Getenv here.
I can draft the setAnsibleConfig helper and the bindEnv entries if you confirm the intended flag names.
examples/demo-ansible/components/ansible/webapp/group_vars/webservers.yml (1)
22-22
: Add newline at EOF.Satisfy YAMLlint’s new-line-at-end-of-file rule.
status_page_location: "/nginx_status" +
examples/demo-ansible/components/ansible/webapp/templates/app.conf.j2 (4)
12-16
: Prefer default_type over add_header for /health Content-TypeSet default_type and drop the header; simpler and avoids surprises.
- location /health { - access_log off; - return 200 "OK - {{ app_name }} v{{ app_version }} ({{ app_env }})\n"; - add_header Content-Type text/plain; - } + location /health { + access_log off; + default_type text/plain; + return 200 "OK - {{ app_name }} v{{ app_version }} ({{ app_env }})\n"; + }
2-2
: Add IPv6 listener for parityExpose the same port on IPv6.
- listen {{ app_port }}; + listen {{ app_port }}; + listen [::]:{{ app_port }};
18-21
: Harden headers (optional)Consider adding CSP, Referrer-Policy, Permissions-Policy, and HSTS (effective over HTTPS).
# Security headers add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "no-referrer" always; + add_header Permissions-Policy "geolocation=()" always; + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + server_tokens off;
22-22
: Add trailing newlineEnds without a newline; some linters complain.
examples/demo-ansible/components/ansible/webapp/inventory.yml (3)
17-17
: Fix YAML lint nits: trailing spaces + EOF newlineTrim trailing spaces (Line 17) and add a newline at EOF (Line 30).
- + @@ - ansible_python_interpreter: /usr/bin/python3 + ansible_python_interpreter: /usr/bin/python3 +Also applies to: 30-30
14-16
: Prefer group_vars over inline vars in inventoryMove group-level vars to group_vars/{webservers,loadbalancers}.yml; keeps inventory focused on host/group membership.
Also applies to: 23-25, 27-30
27-30
: Host key checking disabled — confirm intent'-o StrictHostKeyChecking=no' is fine for demos; avoid in real environments. Consider gating by environment or using Ansible's host key checking setting.
Would you like me to propose a minimal prod-safe variant that keeps this only for non-prod stacks?
examples/demo-ansible/components/ansible/webapp/site.yml (2)
35-41
: Make the symlink creation resilientForce replacement if the dest exists as a file/dir.
- name: Enable app site file: src: "/etc/nginx/sites-available/{{ app_name }}" dest: "/etc/nginx/sites-enabled/{{ app_name }}" state: link + force: yes notify: reload nginx
60-60
: Add trailing newlineEnds without a newline; minor lint warning.
examples/demo-ansible/components/ansible/webapp/group_vars/all.yml (1)
21-21
: Add trailing newlineMinor lint fix.
examples/demo-ansible/stacks/catalog/demo.yaml (1)
22-30
: Consider documenting pass-through flags for Ansible.Readers may expect to pass Ansible flags using
--
(e.g.,--check
,--list
). Suggest adding a short note here mirroring the usage shown later for playbook.examples/demo-ansible/README.md (1)
17-36
: Add a language to the fenced code block (fixes MD040).Use
text
for the directory tree.Apply:
-``` +```text demo-ansible/ ├── atmos.yaml # Atmos configuration ... -``` +```pkg/datafetcher/schema/config/global/1.0.json (1)
607-611
: Outdated component description now that Ansible/Packer are supported.Broaden the
metadata.component
description.- "description": "Terraform/OpenTofu/Helmfile component" + "description": "Component path (e.g., Terraform/OpenTofu/Helmfile/Packer/Ansible)"examples/demo-ansible/schemas/atmos-manifest.json (2)
3-5
: Example schema$id
should not reuse the published manifest$id
.Use a distinct
$id
for the example to avoid clashes during validation or tooling.-"$id": "https://json.schemastore.org/atmos-manifest.json", +"$id": "https://atmos.tools/schemas/examples/atmos-manifest-demo.json",
576-580
: Update component description to include Ansible/Packer.Keep wording consistent across schemas.
- "description": "Terraform/OpenTofu/Helmfile component" + "description": "Component path (e.g., Terraform/OpenTofu/Helmfile/Packer/Ansible)"pkg/datafetcher/schema/stacks/stack-config/1.0.json (1)
607-611
: Outdated component description.Align with added Ansible support.
- "description": "Terraform/OpenTofu/Helmfile component" + "description": "Component path (e.g., Terraform/OpenTofu/Helmfile/Packer/Ansible)"pkg/datafetcher/schema/atmos/manifest/1.0.json (1)
488-573
: Update metadata component description to include Ansible.Minor doc fix: now that Ansible is first‑class, the metadata description should include it (and Packer, if missing) for clarity.
Apply this diff (outside this hunk) to the
metadata.component
description:- "description": "Terraform/OpenTofu/Helmfile component" + "description": "Terraform/OpenTofu/Helmfile/Packer/Ansible component"internal/exec/utils.go (1)
511-521
: Add missing Ansible branch to componentInfo for UX parityIn
internal/exec/utils.go
(around line 511), the switch covers Terraform, Helmfile, and Packer but omits Ansible. SinceconstructAnsibleComponentWorkingDir
exists, add:case cfg.PackerComponentType: componentInfo[cfg.ComponentPathSectionName] = constructPackerComponentWorkingDir(atmosConfig, &configAndStacksInfo) +case cfg.AnsibleComponentType: + componentInfo[cfg.ComponentPathSectionName] = constructAnsibleComponentWorkingDir(atmosConfig, &configAndStacksInfo) }This aligns Ansible with other component types.
cmd/ansible_vault_test.go (1)
1-16
: Nice coverage of static command config.Add
t.Parallel()
to speed up tests and mirror other command tests if applicable.Apply:
func TestAnsibleVaultCmd(t *testing.T) { + t.Parallel() // Test that the command is properly configured
cmd/ansible_inventory_test.go (1)
1-16
: Solid assertions for Cobra wiring.Same minor: add
t.Parallel()
.Apply:
func TestAnsibleInventoryCmd(t *testing.T) { + t.Parallel() // Test that the command is properly configured
cmd/ansible_version.go (1)
14-15
: Polish phrasing.Consider tightening the copy.
- ansibleVersionShort = "Show the version of ansible command." - ansibleVersionLong = `This command shows the version of ansible command. + ansibleVersionShort = "Show the Ansible command version." + ansibleVersionLong = `This command shows the version of the Ansible command.cmd/ansible_version_test.go (1)
9-16
: Add an assertion for Examples to prevent future regressions.If you adopt the
Example
wiring, assert it here.func TestAnsibleVersionCmd(t *testing.T) { // Test that the command is properly configured assert.Equal(t, "version", ansibleVersionCmd.Use) assert.Contains(t, ansibleVersionCmd.Short, "version") assert.Contains(t, ansibleVersionCmd.Long, "version") + assert.Equal(t, ansibleVersionUsageMarkdown, ansibleVersionCmd.Example) assert.True(t, ansibleVersionCmd.FParseErrWhitelist.UnknownFlags) assert.NotNil(t, ansibleVersionCmd.RunE) }
examples/demo-ansible/stacks/deploy/staging/demo.yaml (1)
14-26
: Clean trailing whitespace and add final newline.This quiets yamllint and avoids noisy diffs.
- log_level: "WARNING" - + log_level: "WARNING" # Staging uses different hosts - target_hosts: "webservers" - + target_hosts: "webservers" # Staging environment variables - webapp_environment: "staging" - webapp_version: "1.1.0" - + webapp_environment: "staging" + webapp_version: "1.1.0" settings: ansible: playbook: "site.yml" inventory: "inventory.yml" - env: ANSIBLE_VERBOSITY: "0" ANSIBLE_GATHERING: "explicit" +``` Also applies to: 29-29 </blockquote></details> <details> <summary>pkg/config/config.go (1)</summary><blockquote> `197-204`: **Avoid defaulting to CWD when Ansible base path is unset.** `filepath.Abs("")` yields the current working directory, which can mispoint discovery if `components.ansible.base_path` (or `base_path`) is empty. Prefer leaving it blank unless configured. ```diff - // Convert Ansible dir to an absolute path. - ansibleBasePath := filepath.Join(atmosConfig.BasePath, atmosConfig.Components.Ansible.BasePath) - ansibleDirAbsPath, err := filepath.Abs(ansibleBasePath) - if err != nil { - return err - } - atmosConfig.AnsibleDirAbsolutePath = ansibleDirAbsPath + // Convert Ansible dir to an absolute path (only if configured). + if atmosConfig.Components.Ansible.BasePath != "" || atmosConfig.BasePath != "" { + ansibleBasePath := filepath.Join(atmosConfig.BasePath, atmosConfig.Components.Ansible.BasePath) + ansibleDirAbsPath, err := filepath.Abs(ansibleBasePath) + if err != nil { + return err + } + atmosConfig.AnsibleDirAbsolutePath = ansibleDirAbsPath + }If schema defaults guarantee a non-empty base path for Ansible, disregard.
internal/exec/validate_stacks.go (1)
84-94
: Also validate duplicate Ansible components across manifests.Parity with Terraform/Helmfile helps catch misconfigurations early.
helmfileComponentStackMap, err := createComponentStackMap(atmosConfig, stacksMap, cfg.HelmfileSectionName) if err != nil { return err } errorList, err = checkComponentStackMap(helmfileComponentStackMap) if err != nil { return err } validationErrorMessages = append(validationErrorMessages, errorList...) + // Check Ansible components for duplicates across the same stack + ansibleComponentStackMap, err := createComponentStackMap(atmosConfig, stacksMap, cfg.AnsibleSectionName) + if err != nil { + return err + } + errorList, err = checkComponentStackMap(ansibleComponentStackMap) + if err != nil { + return err + } + validationErrorMessages = append(validationErrorMessages, errorList...)pkg/component/atmos.yaml (1)
51-55
: Ansible block added — confirm command semantics across subcommandsLooks good. One check:
components.ansible.command
is set toansible-playbook
, but inventory and vault use different binaries. If the field is intended as a single “base” command, consider documenting that only playbook uses it, or rename toplaybook_command
to avoid confusion. Also verify the ENV overrides are wired:ATMOS_COMPONENTS_ANSIBLE_COMMAND
andATMOS_COMPONENTS_ANSIBLE_BASE_PATH
.Would you like a small docs snippet clarifying the command’s scope?
internal/exec/describe_affected_utils.go (2)
78-91
: Guard against empty base paths (low risk)If
Components.Ansible.BasePath
isn’t set,filepath.Join(remoteRepoFileSystemPath, basePath, "")
still works, but produces remote root. If that’s unintended, add a quick non-empty check to mirror other defaults logic.
176-191
: Sanity check for changed files listNot Ansible-specific: if
patch.Stats()
grows large, consider short-circuiting early when no files match any component roots to save work.internal/exec/ansible_test.go (2)
11-20
: Flags struct smoke test is fine; consider table-driven coverageAdd a small table test covering empty values and unusual filenames to catch regressions in flag plumbing.
22-36
: Exercise ExecuteAnsible paths via a stub runnerRight now we just assert values. Introduce a minimal exec stub and call
ExecuteAnsible
for subcommands:version
,playbook
,inventory
,vault
, asserting it selects the correct binary/args without running external processes.I can provide a stubbed runner interface and tests if helpful.
examples/demo-ansible/stacks/deploy/dev/demo.yaml (1)
14-14
: Clean up trailing whitespace and add newline at EOFYamllint flags trailing spaces and missing newline. Trim and add EOF newline.
- + - + - + - + - ANSIBLE_DISPLAY_SKIPPED_HOSTS: "True" + ANSIBLE_DISPLAY_SKIPPED_HOSTS: "True" +Also applies to: 17-17, 20-20, 26-26, 29-29
website/docs/cli/commands/ansible/ansible-version.mdx (1)
1-7
: Polish description grammarMinor copyedit for clarity and consistency with Ansible capitalization.
-description: Show the version of ansible command +description: Show the version of the Ansible commandwebsite/docs/cli/commands/ansible/ansible-playbook.mdx (1)
44-55
: Document pass-through of unknown flags to ansible-playbookThe CLI enables UnknownFlags; add a note so users know they can pass native ansible-playbook flags through Atmos.
</dl> +:::note +Any flags unknown to Atmos are passed through to the underlying `ansible-playbook` command. +:::cmd/ansible_vault.go (1)
16-26
: Microcopy nit: tighten phrasing.Consider “Encrypt and decrypt files with Ansible Vault.” for the short description.
internal/exec/path_utils_test.go (1)
148-179
: Optional: add working-dir test for Ansible to match Packer coverage.Parity improves confidence in path utils.
I can draft
TestConstructAnsibleComponentWorkingDir
mirroringTestConstructPackerComponentWorkingDir
.pkg/list/list_components_test.go (1)
120-125
: Nice: fixtures now include packer and ansibleThis broadens coverage across component types.
Add a small negative test with an unknown component type (e.g., "custom": {}) to ensure it’s harmlessly ignored or explicitly handled, matching intended behavior.
pkg/list/list_components.go (1)
23-62
: Centralize error vars per repo conventionPer team learning, static errors should live in errors/errors.go. Move ErrParseStacks, ErrParseComponents, ErrStackNotFound, ErrProcessStack there and import as needed to avoid drift across packages.
I can generate a targeted diff moving these to errors/errors.go if you confirm the target package name/path.
pkg/stack/stack_processor_test.go (1)
387-388
: Add a minimal assertion that ansible wiring is effectiveSince you supply a real ansible components path, assert components["ansible"] presence (when fixtures include it) to exercise the new path.
If fixtures lack ansible for this stack, add a separate test that points at an ansible-enabled stack and asserts non-empty ansible components.
website/docs/cli/commands/ansible/ansible-vault.mdx (3)
1-7
: Avoid potential doc ID collisions"id: vault" might clash with other sections named "vault". Prefer a unique id like "ansible-vault" to keep routes stable.
Apply this tiny change and adjust any inbound links if present.
9-13
: Nit: align import styleEither add semicolons to all import lines or remove the trailing one for consistency with the repo’s MDX style.
25-36
: Consider adding prerequisites and common flagsA short prerequisites note (requires ansible + ansible-vault) and examples with --vault-id/--vault-password-file would make this page immediately actionable.
Happy to draft a snippet aligned with the implemented flags.
internal/exec/path_utils.go (1)
118-145
: Reduce duplication with a small helperThe varfile-name pattern repeats across types. Consider a helper like makeVarfileName(prefix, replaced, comp, suffix string) to centralize formatting.
Example:
func makeVarfileName(ctx, replaced, comp, suffix string) string { if replaced == "" { return fmt.Sprintf("%s-%s.%s", ctx, comp, suffix) } return fmt.Sprintf("%s-%s-%s.%s", ctx, replaced, comp, suffix) }internal/exec/ansible_utils_test.go (3)
80-91
: Prevent subtest loop-capture gotchaShadow the loop variable inside t.Run to avoid future parallelization issues.
- for _, tt := range testCases { - t.Run(tt.name, func(t *testing.T) { + for _, tt := range testCases { + tt := tt + t.Run(tt.name, func(t *testing.T) {
12-18
: Drop unused wantErr or add an error-path caseAll cases set wantErr=false; either remove the field or add at least one case asserting an error.
21-76
: Avoid hardcoded section keys; use config constantsUsing literals like "ansible" risks drift. Prefer cfg.AnsibleSectionName.
@@ -import ( +import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/cloudposse/atmos/pkg/schema" + cfg "github.com/cloudposse/atmos/pkg/config" ) @@ - settings: &schema.AtmosSectionMapType{ - "ansible": map[string]any{ + settings: &schema.AtmosSectionMapType{ + cfg.AnsibleSectionName: map[string]any{ settingKey: validValue, }, }, @@ - settings: &schema.AtmosSectionMapType{ - "ansible": map[string]any{ + settings: &schema.AtmosSectionMapType{ + cfg.AnsibleSectionName: map[string]any{ "other": "value", }, }, @@ - settings: &schema.AtmosSectionMapType{ - "ansible": "invalid", + settings: &schema.AtmosSectionMapType{ + cfg.AnsibleSectionName: "invalid", }, @@ - settings: &schema.AtmosSectionMapType{ - "ansible": map[string]any{ + settings: &schema.AtmosSectionMapType{ + cfg.AnsibleSectionName: map[string]any{ settingKey: 123, }, },cmd/ansible_inventory.go (1)
28-38
: Optional: richer help renderingIf desired, render the markdown in a custom help func.
func init() { ansibleCmd.AddCommand(ansibleInventoryCmd) + ansibleInventoryCmd.SetHelpFunc(func(cmd *cobra.Command, args []string) { + // utils.PrintfMarkdown(ansibleInventoryUsageMarkdown) // uncomment if utils is available + cmd.Parent().HelpFunc()(cmd, args) // fallback to default help + }) }website/docs/cli/commands/ansible/usage.mdx (1)
6-9
: Remove unused importsScreengrab and File aren’t used; trim to avoid bundler noise.
-import Screengrab from '@site/src/components/Screengrab' -import DocCardList from '@theme/DocCardList' -import File from '@site/src/components/File' -import Terminal from '@site/src/components/Terminal' +import DocCardList from '@theme/DocCardList' +import Terminal from '@site/src/components/Terminal' import useBaseUrl from '@docusaurus/useBaseUrl';internal/exec/stack_processor_utils_test.go (1)
630-645
: Label newly added path arg for readabilityComment the base-path params to avoid misordering with the new Ansible arg.
- config, err := ProcessStackConfig( + config, err := ProcessStackConfig( &atmosConfig, - stacksBasePath, - filepath.Join(basePath, "components", "terraform"), - filepath.Join(basePath, "components", "helmfile"), - filepath.Join(basePath, "components", "packer"), - filepath.Join(basePath, "components", "ansible"), + stacksBasePath, // stacksBasePath + filepath.Join(basePath, "components", "terraform"), // terraformComponentsBasePath + filepath.Join(basePath, "components", "helmfile"), // helmfileComponentsBasePath + filepath.Join(basePath, "components", "packer"), // packerComponentsBasePath + filepath.Join(basePath, "components", "ansible"), // ansibleComponentsBasePath "nonprod", deepMergedStackConfig, false, false, "", map[string]map[string][]string{}, importsConfig, true, )pkg/stack/stack_processor.go (1)
104-105
: Pass real base paths instead of empty strings
Replace the empty string placeholders with the actualAtmosConfiguration
fields, which are present in the schema.Apply:
- "", // packerComponentsBasePath - not used in this context - "", // ansibleComponentsBasePath - not used in this context + atmosConfig.PackerDirAbsolutePath, + atmosConfig.AnsibleDirAbsolutePath,internal/exec/ansible_utils.go (2)
9-25
: Return type doesn’t match behavior; also duplicate logic
- The error is always nil; return (string, bool) or emit a typed error when the value exists but is the wrong type.
- The map-walk pattern is duplicated; factor to a tiny helper.
Apply:
package exec @@ -// GetAnsiblePlaybookFromSettings returns an Ansible playbook name from the `settings.ansible.playbook` section in the Atmos component manifest. -func GetAnsiblePlaybookFromSettings(settings *schema.AtmosSectionMapType) (string, error) { - if settings == nil { - return "", nil - } - - var ansibleSection schema.AtmosSectionMapType - var ansiblePlaybook string - var ok bool - - if ansibleSection, ok = (*settings)[cfg.AnsibleSectionName].(map[string]any); !ok { - return "", nil - } - if ansiblePlaybook, ok = ansibleSection[cfg.AnsiblePlaybookSectionName].(string); !ok { - return "", nil - } - return ansiblePlaybook, nil -} +// GetAnsiblePlaybookFromSettings returns settings.ansible.playbook if present. +func GetAnsiblePlaybookFromSettings(settings *schema.AtmosSectionMapType) (string, error) { + return getAnsibleString(settings, cfg.AnsiblePlaybookSectionName) +} + +func getAnsibleString(settings *schema.AtmosSectionMapType, key string) (string, error) { + if settings == nil { + return "", nil + } + section, ok := (*settings)[cfg.AnsibleSectionName].(map[string]any) + if !ok || section == nil { + return "", nil + } + val, ok := section[key] + if !ok { + return "", nil + } + s, ok := val.(string) + if !ok { + return "", fmt.Errorf("settings.ansible.%s must be a string", key) + } + return s, nil +}(Add
fmt
import.)
28-44
: Same concerns for inventory getterLeverage the shared helper to keep behavior consistent and remove duplication.
Apply:
-// GetAnsibleInventoryFromSettings returns an Ansible inventory from the `settings.ansible.inventory` section in the Atmos component manifest. -func GetAnsibleInventoryFromSettings(settings *schema.AtmosSectionMapType) (string, error) { - if settings == nil { - return "", nil - } - - var ansibleSection schema.AtmosSectionMapType - var ansibleInventory string - var ok bool - - if ansibleSection, ok = (*settings)[cfg.AnsibleSectionName].(map[string]any); !ok { - return "", nil - } - if ansibleInventory, ok = ansibleSection[cfg.AnsibleInventorySectionName].(string); !ok { - return "", nil - } - return ansibleInventory, nil -} +// GetAnsibleInventoryFromSettings returns settings.ansible.inventory if present. +func GetAnsibleInventoryFromSettings(settings *schema.AtmosSectionMapType) (string, error) { + return getAnsibleString(settings, cfg.AnsibleInventorySectionName) +}examples/demo-ansible/stacks/deploy/prod/demo.yaml (1)
14-14
: Fix YAML lint: trailing spaces and missing newline
- Remove trailing spaces on lines 14, 19, 23, 28.
- Add a newline at EOF.
Also applies to: 19-19, 23-23, 28-28, 32-32
internal/exec/ansible.go (4)
96-96
: Remove unnecessary error returnThe function always returns nil for error - consider removing the error from the return signature or documenting why it might return an error in the future.
-func prepareAnsibleArgs(info *schema.ConfigAndStacksInfo, playbook, inventory, varFile string) ([]string, error) { +func prepareAnsibleArgs(info *schema.ConfigAndStacksInfo, playbook, inventory, varFile string) []string {Then update the call site at line 235:
- allArgsAndFlags, err := prepareAnsibleArgs(info, playbook, inventory, varFile) - if err != nil { - return err - } + allArgsAndFlags := prepareAnsibleArgs(info, playbook, inventory, varFile)
207-207
: Define named constant for file permissionsMagic number should be a named constant for clarity.
Add at the top of the file:
const ( // AnsibleVarFilePermissions defines the file permissions for Ansible variable files AnsibleVarFilePermissions = 0o644 )Then use it:
- err = u.WriteToFileAsYAML(varFilePath, info.ComponentVarsSection, 0o644) + err = u.WriteToFileAsYAML(varFilePath, info.ComponentVarsSection, AnsibleVarFilePermissions)
260-260
: Define constant for "playbook" stringThe string "playbook" appears multiple times - extract to a constant.
Add constants at the top:
const ( AnsiblePlaybookSubCommand = "playbook" AnsibleInventorySubCommand = "inventory" AnsibleVaultSubCommand = "vault" AnsibleVersionSubCommand = "version" )Then use throughout the file.
135-268
: Consider reducing function complexityThe
ExecuteAnsible
function has high cognitive complexity (23). Consider extracting the variable file handling into a separate function.Extract lines 199-211 into a helper:
func writeComponentVariables(atmosConfig *schema.AtmosConfiguration, info *schema.ConfigAndStacksInfo, varFilePath string) error { if info.DryRun { log.Debug("Dry run: would write variables to file", "file", varFilePath) return nil } log.Debug("Writing the variables to file:", "file", varFilePath) return u.WriteToFileAsYAML(varFilePath, info.ComponentVarsSection, AnsibleVarFilePermissions) }internal/exec/stack_processor_utils.go (2)
682-682
: Use wrapped static errors instead of dynamic errorsDefine static errors and wrap them with additional context instead of using
fmt.Errorf
directly.Add at the package level:
var ( ErrInvalidAnsibleSection = errors.New("invalid 'ansible' section") ErrInvalidAnsibleCommandSection = errors.New("invalid 'ansible.command' section") ErrInvalidAnsibleVarsSection = errors.New("invalid 'ansible.vars' section") ErrInvalidAnsibleSettingsSection = errors.New("invalid 'ansible.settings' section") ErrInvalidAnsibleEnvSection = errors.New("invalid 'ansible.env' section") )Then wrap with context:
- return nil, fmt.Errorf("invalid 'ansible' section in the file '%s'", stackName) + return nil, fmt.Errorf("%w in file '%s'", ErrInvalidAnsibleSection, stackName)Also applies to: 891-891, 898-898, 910-910, 922-922
2063-2063
: Remove unnecessary blank lineThere's an unnecessary leading newline after the comment.
// Process all Ansible components - if componentTypeFilter == "" || componentTypeFilter == cfg.AnsibleComponentType {
"ansible": { | ||
"$ref": "#/definitions/ansible" | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Confirm import
vs imports
usage across schema and code.
The schema exposes root import
, while code paths (e.g., internal/exec/utils.go
) reference imports
. Ensure the loader populates imports
as intended, or align names to avoid confusion.
Run to spot occurrences in repo:
Also applies to: 81-85
🏁 Script executed:
#!/bin/bash
rg -nP '^\s*(import|imports)\s*:' -g '!**/vendor/**' -g '!**/node_modules/**' website examples pkg internal
Length of output: 13340
Align import
vs imports
naming
Manifest schema currently defines a singular import
, but code (e.g. pkg/stack/stack_processor_test.go
) and examples reference imports
. Pick one form—singular or plural—and update the JSON schema, loader (v.GetStringSlice
), tests, and examples to match.
🤖 Prompt for AI Agents
In pkg/datafetcher/schema/atmos/manifest/1.0.json around lines 19-21, the schema
currently uses the singular "import" but the codebase and examples expect
"imports"; change the schema property to "imports" (and update any
$ref/definitions accordingly) so it validates a string array, then update the
loader code that reads this field to use v.GetStringSlice("imports") if it does
not already, and adjust all tests (e.g., pkg/stack/stack_processor_test.go) and
example manifests to use "imports" instead of "import" so names are consistent
across schema, loader, tests, and examples.
terraformComponentsBasePath string, | ||
helmfileComponentsBasePath string, | ||
packerComponentsBasePath string, | ||
ansibleComponentsBasePath string, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Public API just grew again — consider an options struct
Adding ansibleComponentsBasePath here makes the already-long signature harder to maintain. Prefer a single options struct (or functional options) to avoid churn and breaking changes as more component types are added.
Run to find external callsites that may still use the old signature:
🏁 Script executed:
#!/bin/bash
rg -nP '\bProcessYAMLConfigFiles\s*\(' -g '!**/vendor/**' -C2
Length of output: 3990
Update all ProcessYAMLConfigFiles
calls and consider an options struct
- Update every invocation of
ProcessYAMLConfigFiles(...)
in pkg/stack, pkg/spacelift, internal/exec (and their tests) to pass the newansibleComponentsBasePath
argument. - To keep the API manageable as you add more paths, refactor to accept a single options struct or use functional options.
🤖 Prompt for AI Agents
In pkg/stack/stack_processor.go around line 15 the function signature gained a
new parameter ansibleComponentsBasePath, but all callers must be updated to
supply this argument and the API will become harder to manage as more paths are
added; update every invocation of ProcessYAMLConfigFiles(...) across pkg/stack,
pkg/spacelift, internal/exec and their tests to pass the new
ansibleComponentsBasePath value, and refactor the function signature to accept a
single options struct (or implement functional options) instead of many
positional parameters so adding future paths is backwards-compatible; update
call sites to construct and pass the options struct (or use the option
functions) and run tests to ensure compilation and behavior remain correct.
{ | ||
"label": "ansible", | ||
"position": 8, | ||
"className": "command", | ||
"collapsible": true, | ||
"collapsed": true, | ||
"link": { | ||
"type": "doc", | ||
"id": "usage" | ||
} | ||
} No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Doc ID may be too short; prefer full doc ID.
Unless usage.mdx
sets id: usage
in front matter, Docusaurus expects the full ID (path-based). Use the absolute ID to be safe.
Apply:
- "link": {
- "type": "doc",
- "id": "usage"
- }
+ "link": {
+ "type": "doc",
+ "id": "cli/commands/ansible/usage"
+ }
Check whether usage.mdx
declares id: usage
:
🏁 Script executed:
#!/bin/bash
fd -a usage.mdx website/docs | xargs -I{} sh -c 'echo "== {} =="; sed -n "1,20p" {} | rg -n "^id:\s*usage\b" || true'
Length of output: 939
Use full Docusaurus doc ID for Ansible usage link
The usage.mdx
file doesn’t declare id: usage
, so Docusaurus will use its path-based ID (cli/commands/ansible/usage
). Update the JSON to reference the absolute ID:
- "link": {
- "type": "doc",
- "id": "usage"
- }
+ "link": {
+ "type": "doc",
+ "id": "cli/commands/ansible/usage"
+ }
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
{ | |
"label": "ansible", | |
"position": 8, | |
"className": "command", | |
"collapsible": true, | |
"collapsed": true, | |
"link": { | |
"type": "doc", | |
"id": "usage" | |
} | |
} | |
{ | |
"label": "ansible", | |
"position": 8, | |
"className": "command", | |
"collapsible": true, | |
"collapsed": true, | |
"link": { | |
"type": "doc", | |
"id": "cli/commands/ansible/usage" | |
} | |
} |
🤖 Prompt for AI Agents
In website/docs/cli/commands/ansible/_category_.json around lines 1-11, the
category link uses a relative doc id "usage" which doesn't match the actual
Docusaurus path-based id for usage.mdx; update the link.id to the full absolute
doc id "cli/commands/ansible/usage" so the category correctly links to the
Ansible usage page.
Also, it's good to have an example as you have, but I want to make sure we don't overload the purpose of the examples. We have a tests/fixtures directory where we create our reusable fixtures, specifically for testing different scenarios. Reach out if you need more help understanding how we organize that. Also note that we keep component fixtures deliberately separate from stack fixtures. This allows us to reuse component fixtures in multiple scenarios, without duplicating the boilerplate. |
Make sure the following commands work with the implementation.
|
- Remove trailing whitespace throughout CLAUDE.md and other files - Add Example field to ansible-related commands - Implement setAnsibleConfig function to handle command-line args - Update component tests to include nginx component - Fix typo in ansible_vault.go long description - Add fields for AnsibleCommand and AnsibleDir to schema structs
– Execute ansible commands for configuration management | ||
|
||
``` | ||
$ atmos ansible playbook <component-name> -s <stack-name> | ||
``` | ||
|
||
``` | ||
$ atmos ansible inventory <component-name> -s <stack-name> --list | ||
``` | ||
|
||
``` | ||
$ atmos ansible vault <component-name> -s <stack-name> encrypt <file> | ||
``` No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't just list off a bunch of commands, list what each command is doing in plain English following the format of the other usage examples.
|
||
components: | ||
ansible: | ||
webapp: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should it support a first-class inventory
?
Co-authored-by: Erik Osterman (CEO @ Cloud Posse) <[email protected]>
atmosConfig.Components.Packer.BasePath = componentsPackerBasePath | ||
} | ||
|
||
componentsAnsibleCommand := os.Getenv("ATMOS_COMPONENTS_ANSIBLE_COMMAND") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs switching out for the equivalent viper call
atmosConfig.Components.Ansible.Command = componentsAnsibleCommand | ||
} | ||
|
||
componentsAnsibleBasePath := os.Getenv("ATMOS_COMPONENTS_ANSIBLE_BASE_PATH") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this needs switching out for the equivalent viper call
// ErrParseComponents is returned when component data cannot be parsed. | ||
ErrParseComponents = errors.New("could not parse components") | ||
// ErrParseTerraformComponents is returned when terraform component data cannot be parsed. | ||
ErrParseTerraformComponents = errors.New("could not parse Terraform components") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
was this intended to be lost?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
many whitespace diffs in this file, if it is only whitespace and nothing else, revert the diffs
Closing this draft PR, for now, as we mature the PRD and vision for what an Atmos/Ansible integration looks like. I'll gladly work with any other members of the community who are interested in developing this capability. |
what
ansible
command and subcommands forplaybook
,inventory
,vault
, andversion
in thecmd/
package, each with its own usage, help, and integration with Atmos stack/component configuration.ansible
command and delegates execution to a shared handler, supporting relevant flags and options for Ansible operations.examples
map for help suggestions in the CLI.examples/demo-ansible
showing how to structure Ansible components, stacks, settings, and environment variables within Atmos, including playbooks, inventory, and configuration files.why
references
Note
This PR is primarily based on the Packer implementation in this PR
Summary by CodeRabbit
New Features
Documentation
Tests
Chores