-
-
Notifications
You must be signed in to change notification settings - Fork 107
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
Refine Atmos Configuration Handling to Support Imports, Remote Imports, and Configuration Directories #808
base: main
Are you sure you want to change the base?
Conversation
📝 WalkthroughConfiguration Import and Loading EnhancementsWalkthroughThis pull request introduces substantial improvements to the Atmos CLI's configuration management system. The changes focus on enhancing configuration loading flexibility, supporting a new import mechanism, and improving environment variable handling. Key modifications include adding support for importing configurations from multiple sources (local files, directories, and remote URLs), implementing a robust configuration merging strategy, and setting critical environment variables like Changes
Assessment against linked issues
Possibly related PRs
Suggested reviewersBy the gods of configuration management, we shall bring order to the chaos of file imports! 🏛️🔧 Finishing Touches
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
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: 9
🧹 Outside diff range and nitpick comments (7)
examples/demo-atmos.d/atmos.yaml (3)
4-11
: Document security-sensitive settingsCritical settings like
apply_auto_approve
andauto_generate_backend_file
would benefit from inline documentation explaining their security implications.Consider adding comments:
components: terraform: base_path: "components/terraform" + # Disable auto-approve for safety in production environments apply_auto_approve: false deploy_run_init: true init_run_reconfigure: true + # Disable auto-generation of backend files to prevent unauthorized state access auto_generate_backend_file: false
12-19
: Consider expanding name pattern flexibilityThe current name pattern only supports {stage}. Consider supporting additional variables for better organization.
Consider expanding:
- name_pattern: "{stage}" + name_pattern: "{org}-{stage}-{component}"
20-29
: Clean up formatting and enhance documentationThe vendor configuration contains helpful examples but needs formatting cleanup.
Apply these changes:
-vendor: +vendor: # Single file - base_path: "./vendor.yaml" + base_path: "./vendor.yaml" # Default configuration # Directory with multiple files #base_path: "./vendor" # Absolute path #base_path: "vendor.d/vendor1.yaml"🧰 Tools
🪛 yamllint (1.29.0-1)
[error] 20-20: trailing spaces
(trailing-spaces)
[error] 23-23: trailing spaces
(trailing-spaces)
[error] 26-26: trailing spaces
(trailing-spaces)
examples/demo-atmos.d/custom-import/atmos.yaml (2)
26-35
: Consider moving configuration examples to documentationWhile the commented examples are helpful, they might be better suited in the documentation, keeping the configuration file cleaner.
- # Single file base_path: "./vendor.yaml" - - # Directory with multiple files - #base_path: "./vendor" - - # Absolute path - #base_path: "vendor.d/vendor1.yaml"🧰 Tools
🪛 yamllint (1.29.0-1)
[error] 26-26: trailing spaces
(trailing-spaces)
[error] 29-29: trailing spaces
(trailing-spaces)
[error] 32-32: trailing spaces
(trailing-spaces)
36-38
: Consider environment-specific log configurationsThe current logging setup is good for development, but you might want to add environment-specific configurations for production use.
Example addition:
logs: file: "${ATMOS_LOG_FILE:-/dev/stderr}" level: "${ATMOS_LOG_LEVEL:-Info}"pkg/schema/schema.go (1)
29-29
: Add field documentation.Consider adding a documentation comment to describe the purpose and usage of the
Import
field, following Go's documentation conventions.Add this documentation above the field:
+ // Import specifies a list of paths from which to import additional configurations. + // Supports local files, directories (using glob patterns), and remote URLs. Import []string `yaml:"import" json:"import" mapstructure:"import"`pkg/config/config.go (1)
231-232
: Combine the return statement for clarityThe return statement is unnecessarily split across two lines.
Consider writing it on a single line:
-return cliConfig, - err +return cliConfig, err
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (10)
examples/demo-atmos.d/atmos.d/configs.d/config1.yaml
(1 hunks)examples/demo-atmos.d/atmos.d/configs.d/sub/config2.yaml
(1 hunks)examples/demo-atmos.d/atmos.yaml
(1 hunks)examples/demo-atmos.d/custom-import/atmos.yaml
(1 hunks)examples/demo-atmos.d/custom-import/configs.d/config1.yaml
(1 hunks)examples/demo-atmos.d/custom-import/configs.d/sub/config2.yaml
(1 hunks)examples/demo-atmos.d/custom-import/extra-config.yaml
(1 hunks)pkg/config/config.go
(3 hunks)pkg/schema/schema.go
(1 hunks)pkg/utils/glob_utils.go
(2 hunks)
✅ Files skipped from review due to trivial changes (5)
- examples/demo-atmos.d/atmos.d/configs.d/config1.yaml
- examples/demo-atmos.d/atmos.d/configs.d/sub/config2.yaml
- examples/demo-atmos.d/custom-import/configs.d/config1.yaml
- examples/demo-atmos.d/custom-import/configs.d/sub/config2.yaml
- examples/demo-atmos.d/custom-import/extra-config.yaml
🧰 Additional context used
🪛 yamllint (1.29.0-1)
examples/demo-atmos.d/atmos.yaml
[error] 20-20: trailing spaces
(trailing-spaces)
[error] 23-23: trailing spaces
(trailing-spaces)
[error] 26-26: trailing spaces
(trailing-spaces)
[warning] 38-38: wrong indentation: expected 2 but found 0
(indentation)
[warning] 41-41: wrong indentation: expected 4 but found 2
(indentation)
examples/demo-atmos.d/custom-import/atmos.yaml
[error] 26-26: trailing spaces
(trailing-spaces)
[error] 29-29: trailing spaces
(trailing-spaces)
[error] 32-32: trailing spaces
(trailing-spaces)
[warning] 44-44: wrong indentation: expected 2 but found 0
(indentation)
[warning] 47-47: wrong indentation: expected 4 but found 2
(indentation)
🔇 Additional comments (6)
examples/demo-atmos.d/atmos.yaml (1)
30-33
: Add validation for log levels
Consider adding validation to ensure only valid log levels (Debug, Info, Warning, Error) are accepted.
examples/demo-atmos.d/custom-import/atmos.yaml (3)
7-9
: Consider adding URL validation for remote configurations
While loading from remote URLs provides flexibility, it could pose security risks. Consider implementing URL validation and HTTPS enforcement.
Would you like me to propose a validation pattern for secure URL handling?
18-24
: Strong stack configuration, warrior! 💪
The stack configuration is well-structured with clear inclusion/exclusion patterns and a simple naming convention.
11-16
: Verify backend file management strategy
Setting auto_generate_backend_file: false
means you'll need to manage backend configurations manually. This is a valid choice but requires careful consideration.
Run this check to ensure consistent backend configuration across components:
✅ Verification successful
Setting auto_generate_backend_file: false
is appropriate for this setup
The configuration choice is valid as there's no existing Terraform component directory at the specified path, indicating this is likely a new project setup. Manual backend management is a reasonable approach when starting fresh, allowing for more controlled and explicit backend configuration as the project evolves.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Check for existing backend configurations
fd -e tf -e hcl . components/terraform -x grep -l 'backend'
Length of output: 167
Script:
#!/bin/bash
# First, let's verify if the components/terraform directory exists and check the directory structure
fd --type d terraform
# Then, let's search for backend configurations across all Terraform files in the repository
fd -e tf -e hcl . -x grep -l 'backend'
# Also check for any Terraform component references in the atmos configuration
fd -g "*.yaml" -x grep -A 5 -B 5 "terraform:"
Length of output: 33961
Script:
#!/bin/bash
# Let's check for backend configurations in the actual Terraform components
fd -t f -e tf . examples/demo-atmos.d/custom-import/components/terraform -x grep -l 'backend'
# Also check for any backend configuration files
fd -t f -g "*.backend.tf" -g "backend.tf" examples/demo-atmos.d/custom-import/components/terraform
# Check if there are any existing backend configurations in the component directory
ls -la examples/demo-atmos.d/custom-import/components/terraform/
Length of output: 709
pkg/utils/glob_utils.go (2)
7-7
: LGTM! Clean import addition, warrior!
The sort package import is well-placed and necessary for the new functionality.
Line range hint 19-48
: Verify caching behavior for dynamic configurations, brave one!
The caching mechanism using sync.Map could prevent picking up new configuration files added to atmos.d during runtime. Additionally, we should verify that the error handling aligns with the requirement to log non-existent paths as warnings.
Let's verify the implementation:
✅ Verification successful
Cache invalidation is not a concern, brave warrior!
The caching mechanism in GetGlobMatches
is pattern-based, which means:
- Each unique glob pattern gets its own cache entry
- When a new file is added that matches an existing pattern, it will be discovered on the next process restart
- The cache is process-scoped (using
sync.Map
), so it's automatically cleared between runs - Error handling correctly returns both the error and logs the non-existent paths
The implementation aligns perfectly with the requirements:
- Cache is scoped to the current process, preventing stale state between runs
- Error handling returns descriptive errors for non-existent paths:
failed to find a match for the import '%s' ('%s' + '%s')
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Check for warning logs related to non-existent paths and cache invalidation
# Search for warning log implementations
rg -A 2 "failed to find a match for the import" .
# Check if there's any cache invalidation mechanism
rg -A 5 "getGlobMatchesSyncMap" .
# Look for any warning level log statements
rg -A 2 "Warning|WARN|warn" .
Length of output: 47400
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: 3
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (2)
pkg/config/config.go
(3 hunks)pkg/schema/schema.go
(1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
- pkg/schema/schema.go
🔇 Additional comments (1)
pkg/config/config.go (1)
231-232
: LGTM! Clean error handling.
The multi-line error return improves readability.
…correct command structure
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: 0
🧹 Outside diff range and nitpick comments (3)
examples/demo-atmos.d/custom-import/atmos.yaml (2)
26-34
: Clean up trailing spacesRemove trailing spaces from lines 26, 29, and 32 for better maintainability.
-vendor: +vendor: # Single file -base_path: "./vendor.yaml" +base_path: "./vendor.yaml" # Directory with multiple files -#base_path: "./vendor" +#base_path: "./vendor"🧰 Tools
🪛 yamllint (1.29.0-1)
[error] 26-26: trailing spaces
(trailing-spaces)
[error] 29-29: trailing spaces
(trailing-spaces)
[error] 32-32: trailing spaces
(trailing-spaces)
36-38
: Logging configuration is good but could be enhancedCurrent configuration is solid. Consider adding:
- Log rotation settings
- Format specification
- Additional log destinations for production environments
pkg/config/config.go (1)
401-444
: Consider adding rate limiting and path validationThe import processing is robust, but consider these enhancements:
- Add rate limiting for remote downloads to prevent abuse
- Validate import paths before processing to ensure they're safe
func processImports(cliConfig schema.CliConfiguration, v *viper.Viper) error { + // Add rate limiting for remote downloads + rateLimiter := time.NewTicker(time.Second) + defer rateLimiter.Stop() + for _, importPath := range cliConfig.Import { if importPath == "" { continue } + // Basic path validation + if strings.Contains(importPath, "..") { + u.LogWarning(cliConfig, fmt.Sprintf("Warning: skipping potentially unsafe import path '%s'", importPath)) + continue + }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (4)
examples/demo-atmos.d/atmos.d/config1.yaml
(1 hunks)examples/demo-atmos.d/atmos.d/sub/config2.yaml
(1 hunks)examples/demo-atmos.d/custom-import/atmos.yaml
(1 hunks)pkg/config/config.go
(3 hunks)
✅ Files skipped from review due to trivial changes (2)
- examples/demo-atmos.d/atmos.d/config1.yaml
- examples/demo-atmos.d/atmos.d/sub/config2.yaml
🧰 Additional context used
🪛 yamllint (1.29.0-1)
examples/demo-atmos.d/custom-import/atmos.yaml
[error] 26-26: trailing spaces
(trailing-spaces)
[error] 29-29: trailing spaces
(trailing-spaces)
[error] 32-32: trailing spaces
(trailing-spaces)
🔇 Additional comments (7)
examples/demo-atmos.d/custom-import/atmos.yaml (4)
11-16
: Terraform security settings look good!
The Terraform component configuration follows security best practices:
apply_auto_approve: false
prevents unintended infrastructure changesauto_generate_backend_file: false
ensures explicit backend configuration
18-24
: Stack configuration structure is well-organized!
Good use of:
- Explicit path inclusions/exclusions
- Clear naming pattern
- Logical directory structure
43-47
: Command configuration looks good!
The test command is well-defined with clear description and steps.
7-9
: Verify security measures for remote configuration imports
The remote URL import capability could pose security risks. Ensure that:
- HTTPS is enforced for remote URLs
- URL validation is implemented
- Content verification is performed before applying configurations
Consider implementing:
- URL allowlist/blocklist
- SSL certificate validation
- Content integrity checks (e.g., checksums)
pkg/config/config.go (3)
231-232
: Clean error handling format!
The multi-line error return improves readability while maintaining functionality.
239-250
: Validate path to prevent directory traversal
While the implementation correctly handles the atmos.d directory existence, consider adding path validation as suggested in the past review to prevent potential path traversal attacks.
446-464
: Add safeguards to remote downloads
The download function needs the safeguards mentioned in the past review:
- Context timeout
- File size limits
- Temporary directory cleanup
…onfig and processImports
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: 0
🧹 Outside diff range and nitpick comments (4)
pkg/config/config.go (4)
231-232
: Consider using idiomatic Go error handling.While splitting the return statement improves readability, consider using the more idiomatic Go style:
- return cliConfig, - err + return cliConfig, fmt.Errorf("failed to unmarshal config: %w", err)
239-256
: Strong implementation with robust security measures!The atmos.d directory support is well-implemented with proper path validation and error handling. The path traversal prevention is particularly noteworthy.
Consider adding more context to the error message:
- return cliConfig, fmt.Errorf("invalid atmos.d path: attempted directory traversal") + return cliConfig, fmt.Errorf("invalid atmos.d path '%s': attempted directory traversal outside base path '%s'", atmosDPath, basePath)
257-269
: Consider adding debug logging for configuration merging.The import processing logic is sound, but adding debug logs would help troubleshoot configuration issues.
err = v.Unmarshal(&cliConfig) if err != nil { + u.LogTrace(cliConfig, fmt.Sprintf("Failed to unmarshal merged config: %v", err)) return cliConfig, err } + u.LogTrace(cliConfig, "Successfully merged and unmarshaled imported configurations")
407-459
: Consider parallel processing for multiple imports.The implementation is robust with proper error handling and path validation. However, processing imports sequentially might be slow for many imports.
Consider using a worker pool for parallel processing:
func processImports(cliConfig schema.CliConfiguration, v *viper.Viper) error { + type importResult struct { + path string + err error + } + workers := make(chan struct{}, 4) // Limit concurrent downloads + results := make(chan importResult) + for _, importPath := range cliConfig.Import { - if importPath == "" { - continue - } + go func(imp string) { + workers <- struct{}{} // Acquire worker + defer func() { <-workers }() // Release worker + + if imp == "" { + results <- importResult{"", nil} + return + } + // ... rest of the import processing logic ... + }(importPath) }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (1)
pkg/config/config.go
(3 hunks)
🔇 Additional comments (1)
pkg/config/config.go (1)
461-479
: Past review comment about remote download safeguards is still applicable.
The previous review comment suggesting context timeout, file size limits, and temporary directory cleanup remains valid.
…andle cleanup on error
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: 0
🧹 Outside diff range and nitpick comments (1)
pkg/config/config.go (1)
232-233
: Combine the return statement into a single lineThe return statement is split across two lines. Merging it into one line enhances readability.
Apply this diff:
- return cliConfig, - err + return cliConfig, err
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
📒 Files selected for processing (1)
pkg/config/config.go
(3 hunks)
🔇 Additional comments (4)
pkg/config/config.go (4)
240-257
: Good handling of the 'import' key and path security
The logic for setting the default Import
path when it's empty is well-implemented. Ensuring the path doesn't escape the intended directory adds a valuable security check.
259-270
: Import processing added effectively
The addition of import processing and re-unmarshalling is correctly handled. Error checks are appropriately placed.
417-426
: Consider validating remote URLs for security
Currently, any URL starting with "http://" or "https://" is accepted. Validating these URLs against an allowlist can prevent unintended or malicious imports.
464-484
: Add a file size limit check in downloadRemoteConfig
While the context timeout is set, the function lacks a file size limit check. This could allow the download of excessively large files, leading to resource exhaustion.
Update documentation for Atmos CLI Configuration schema and examples |
Update documentation for Atmos CLI Configuration |
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: 0
🧹 Nitpick comments (3)
website/docs/cli/configuration/configuration.mdx (3)
27-29
: Add security considerations for configuration loading.While the configuration loading process is well documented, consider adding security implications when loading configurations from different sources, especially for CI/CD environments.
Add a security note after line 45:
You can pass the `--config` flag multiple times in a single command; the configurations will be deep merged in the order you provide them. + :::warning Security Note + When using multiple configuration files, especially in CI/CD environments: + - Validate the source and permissions of configuration files + - Consider using checksums for critical configurations + - Be cautious with configuration files from untrusted sources + :::Also applies to: 39-45
191-196
: Add examples for path resolution.Consider adding practical examples to illustrate the difference between absolute and relative path resolution.
Add examples after line 196:
When `base_path` is explicitly set in the Atmos configuration, it is always resolved to an absolute path based on the current working directory from which `atmos` is executed, unless an absolute path is already provided. + For example: + ```yaml + # Relative path (resolved relative to current working directory) + base_path: "./infrastructure" + + # Absolute path (used as-is) + base_path: "/opt/atmos/infrastructure" + ```
Line range hint
839-867
: Add clarification about environment variable precedence.The environment variables table is comprehensive, but it would be helpful to explicitly state the precedence rules between environment variables and YAML configuration.
Add a note before the table:
+ :::note Environment Variable Precedence + When both environment variables and YAML configuration settings are present: + 1. Command-line arguments take highest precedence + 2. Environment variables override YAML configuration + 3. YAML configuration serves as the default + :::
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
website/docs/cli/configuration/configuration.mdx
(4 hunks)
🔇 Additional comments (2)
website/docs/cli/configuration/configuration.mdx (2)
13-13
: LGTM! Clear and well-structured introduction.The addition of the Admonition component and the improved introduction provide better context for users about the CLI configuration's purpose and scope.
Also applies to: 21-23
179-180
: LGTM! Clear documentation about templating limitations.The explicit note about templating limitations in imports helps prevent confusion and potential issues.
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: 0
🧹 Nitpick comments (4)
website/docs/cli/configuration/configuration.mdx (4)
13-23
: Add missing comma in the example.A comma should be added after "e.g." for proper punctuation.
-...vior) you'd specify the command to run (e.g. [`opentofu`](/core-concepts/projects/co... +...vior) you'd specify the command to run (e.g., [`opentofu`](/core-concepts/projects/co...🧰 Tools
🪛 LanguageTool
[style] ~23-~23: A comma is missing here.
Context: ...vior) you'd specify the command to run (e.g. [opentofu
](/core-concepts/projects/co...(EG_NO_COMMA)
149-187
: Consider adding examples for each import type.While the imports section is well-documented, it would be more helpful to have specific examples for each import type (Remote URL, Specific Path, Wildcard globs).
Consider adding examples like this:
import: # Remote URL example - "https://raw.githubusercontent.com/org/repo/main/config.yaml" # Specific Path example - "/absolute/path/to/config.yaml" - "./relative/path/config.yaml" # Wildcard glob examples - "configs/*.yaml" # Match all YAML files in configs directory - "stacks/**/*.yaml" # Match all YAML files recursively in stacks directory🧰 Tools
🪛 LanguageTool
[grammar] ~181-~181: Did you mean “is” or “can be”?
Context: ... supported (unlike stacks). :::warning Be Careful with Remote Imports - Always us...(BE_IS)
Line range hint
842-887
: Environment variables table needs alignment fix.The environment variables table has some formatting inconsistencies in the markdown.
- Lines 842 and 866 have extra newlines that break table formatting
- Consider using consistent column widths for better readability
Also, consider adding a note about the case sensitivity of these environment variables.
🧰 Tools
🪛 LanguageTool
[style] ~875-~875: Consider a shorter alternative to avoid wordiness.
Context: ...with certain environment variables set, in order to enable the user to use other tools (in ...(IN_ORDER_TO_PREMIUM)
875-877
: Simplify the wording for better clarity.The sentence is wordy and can be simplified.
-spawn an interactive shell with certain environment variables set, in order to enable the user to use other tools +(in the case of `atmos terraform shell`, the Terraform or Tofu CLI) +spawn an interactive shell with certain environment variables set to enable using other tools🧰 Tools
🪛 LanguageTool
[style] ~875-~875: Consider a shorter alternative to avoid wordiness.
Context: ...with certain environment variables set, in order to enable the user to use other tools (in ...(IN_ORDER_TO_PREMIUM)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
website/docs/cli/configuration/configuration.mdx
(4 hunks)
🧰 Additional context used
🪛 LanguageTool
website/docs/cli/configuration/configuration.mdx
[style] ~23-~23: A comma is missing here.
Context: ...vior) you'd specify the command to run (e.g. [opentofu
](/core-concepts/projects/co...
(EG_NO_COMMA)
[style] ~119-~119: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...### Stage 3: Apply User Preferences :::important If the --config
flag is used, this st...
(EN_REPEATEDWORDS_IMPORTANT)
[grammar] ~181-~181: Did you mean “is” or “can be”?
Context: ... supported (unlike stacks). :::warning Be Careful with Remote Imports - Always us...
(BE_IS)
[style] ~875-~875: Consider a shorter alternative to avoid wordiness.
Context: ...with certain environment variables set, in order to enable the user to use other tools (in ...
(IN_ORDER_TO_PREMIUM)
🔇 Additional comments (2)
website/docs/cli/configuration/configuration.mdx (2)
27-146
: Well-structured configuration stages documentation!The three-stage configuration loading process is clearly explained with proper precedence rules and examples. The use of admonitions for important notes about the
--config
flag is particularly helpful.🧰 Tools
🪛 LanguageTool
[style] ~119-~119: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...### Stage 3: Apply User Preferences :::important If the--config
flag is used, this st...(EN_REPEATEDWORDS_IMPORTANT)
189-215
: Excellent base path documentation with clear precedence rules.The base path section effectively explains:
- How the base path is determined
- The order of precedence
- The relationship with other paths
💥 This pull request now has conflicts. Could you fix it @haitham911? 🙏 |
This PR was closed due to inactivity and merge conflicts. 😭 |
💥 This pull request now has conflicts. Could you fix it @haitham911? 🙏 |
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.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
go.mod (1)
Line range hint
3-3
: Invalid Go version specifiedThe Go version
1.23
is not valid as it hasn't been released yet. The latest stable version is Go 1.22.-go 1.23 +go 1.22
🧹 Nitpick comments (14)
pkg/config/home.go (4)
13-13
: Consider migrating from pkg/errors to standard errors package.The
github.com/pkg/errors
package is being deprecated. Since Go 1.13+, the standarderrors
package provides similar functionality with wrapped errors.- "github.com/pkg/errors" + "errors"
42-47
: Consider defining OS constants for better maintainability.Define constants for OS names to avoid magic strings and improve maintainability.
+const ( + windowsOS = "windows" + darwinOS = "darwin" + plan9OS = "plan9" +) - if runtime.GOOS == "windows" { + if runtime.GOOS == windowsOS { result, err = dirWindows() } else { // Unix-like system, so just assume Unix result, err = dirUnix() }
90-147
: Enhance error handling and add command timeouts.While the implementation is thorough, consider these improvements:
- Wrap errors with context
- Add timeouts for command execution
+import "context" +import "time" func dirUnix() (string, error) { + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() ... - if err := cmd.Run(); err != nil { + if err := cmd.Run(); err != nil { + return "", fmt.Errorf("failed to execute getent: %w", err) ... - cmd := exec.Command("sh", "-c", "cd && pwd") + cmd := exec.CommandContext(ctx, "sh", "-c", "cd && pwd")
149-168
: Consider normalizing Windows paths.The implementation could benefit from path normalization to handle potential path separator issues.
drive := os.Getenv("HOMEDRIVE") path := os.Getenv("HOMEPATH") - home := drive + path + home := filepath.Join(drive, path)pkg/config/con.go (1)
87-89
: Error handling improvementIn the function
loadSystemAndUserConfigs
, the error returned at line 89 is not wrapped with context. Providing additional context can aid in debugging.Consider wrapping the error to include more information.
-return cl.atmosConfig, err +return cl.atmosConfig, fmt.Errorf("failed to load system and user configs: %w", err)pkg/config/con_test.go (2)
266-270
: Simplify error expectations in testIn the test case starting at line 266, the error handling can be improved for readability.
Simplify the assertion to make it more concise.
-if err == nil { - t.Fatal("Expected an error for invalid URL, got nil") -} +assert.Error(t, err, "Expected an error for invalid URL")
108-108
: Redundant defer statementThe
defer os.RemoveAll(tmpDir)
at line 108 is unnecessary sincet.TempDir()
automatically handles cleanup.Remove the redundant defer statement.
-defer os.RemoveAll(tmpDir)
pkg/config/utils.go (3)
782-782
: Simplify condition by returning earlyAt line 782, the function returns
false, "", nil
when the file does not exist. This pattern can be simplified for clarity.Consider returning immediately upon detecting the file does not exist.
if os.IsNotExist(err) { return false, "", nil }
808-811
: Error handling improvementIn
ProcessConfigFile
, errors fromv.MergeInConfig()
are wrapped but could include more context.Enhance the error message to provide better context.
-return false, absPath, fmt.Errorf("error reading config file (%s): %w", absPath, err) +return false, absPath, fmt.Errorf("failed to merge config from file '%s': %w", absPath, err)
819-825
: Error message clarityIn
GetHomeDir
, the error messages could be more informative to aid troubleshooting.Adjust the error messages for clarity.
-return "", fmt.Errorf("unable to determine home directory: %w", err) +return "", fmt.Errorf("failed to determine user's home directory: %w", err)website/docs/cli/configuration/configuration.mdx (2)
21-23
: Add missing comma after "e.g."For proper punctuation, add a comma after "e.g." in the sentence.
- Think of this file as where you [bootstrap the settings or configuration of your project](/core-concepts/projects). If you'll be using [terraform](/core-concepts/components/terraform), then [this is where](/cli/configuration/components#terraform-component-behavior) you'd specify the command to run (e.g. [`opentofu`](/core-concepts/projects/configuration/opentofu)), the base path location of the components, and so forth. + Think of this file as where you [bootstrap the settings or configuration of your project](/core-concepts/projects). If you'll be using [terraform](/core-concepts/components/terraform), then [this is where](/cli/configuration/components#terraform-component-behavior) you'd specify the command to run (e.g., [`opentofu`](/core-concepts/projects/configuration/opentofu)), the base path location of the components, and so forth.🧰 Tools
🪛 LanguageTool
[style] ~23-~23: A comma is missing here.
Context: ...vior) you'd specify the command to run (e.g. [opentofu
](/core-concepts/projects/co...(EG_NO_COMMA)
838-838
: Simplify wordy phraseConsider making the sentence more concise by replacing "in order to" with "to".
- spawn an interactive shell with certain environment variables set, in order to enable the user to use other tools + spawn an interactive shell with certain environment variables set to enable the user to use other tools🧰 Tools
🪛 LanguageTool
[style] ~838-~838: Consider a shorter alternative to avoid wordiness.
Context: ...with certain environment variables set, in order to enable the user to use other tools (in ...(IN_ORDER_TO_PREMIUM)
pkg/config/atmos.yaml (2)
85-92
: Consider enhancing log level documentation.While the log configuration is clear, consider adding examples of when each log level should be used.
346-350
: Consider adding retry logic for version checks.While the version check configuration is good, consider adding retry logic for resilience against temporary network issues.
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sum
is excluded by!**/*.sum
📒 Files selected for processing (10)
go.mod
(1 hunks)pkg/config/ReadMe.md
(1 hunks)pkg/config/atmos.yaml
(1 hunks)pkg/config/con.go
(1 hunks)pkg/config/con_test.go
(1 hunks)pkg/config/config_test.go
(1 hunks)pkg/config/home.go
(1 hunks)pkg/config/utils.go
(2 hunks)pkg/utils/file_utils.go
(1 hunks)website/docs/cli/configuration/configuration.mdx
(4 hunks)
✅ Files skipped from review due to trivial changes (2)
- pkg/config/config_test.go
- pkg/utils/file_utils.go
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/config/con_test.go
244-244: Error return value of w.Write
is not checked
(errcheck)
pkg/config/con.go
660-660: printf: non-constant format string in call to fmt.Errorf
(govet)
838-838: printf: non-constant format string in call to fmt.Sprintf
(govet)
773-773: SA4017: HasSuffix doesn't have side effects and its return value is ignored
(staticcheck)
🪛 LanguageTool
website/docs/cli/configuration/configuration.mdx
[style] ~23-~23: A comma is missing here.
Context: ...vior) you'd specify the command to run (e.g. [opentofu
](/core-concepts/projects/co...
(EG_NO_COMMA)
[style] ~119-~119: This word has been used in one of the immediately preceding sentences. Using a synonym could make your text more interesting to read, unless the repetition is intentional.
Context: ...### Stage 3: Apply User Preferences :::important If the --config
flag is used, this st...
(EN_REPEATEDWORDS_IMPORTANT)
[grammar] ~181-~181: Did you mean “is” or “can be”?
Context: ... supported (unlike stacks). :::warning Be Careful with Remote Imports - Always us...
(BE_IS)
[style] ~838-~838: Consider a shorter alternative to avoid wordiness.
Context: ...with certain environment variables set, in order to enable the user to use other tools (in ...
(IN_ORDER_TO_PREMIUM)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Summary
🔇 Additional comments (19)
go.mod (1)
8-8
: Solid choice with the xdg package! 💪The addition of
github.com/adrg/xdg
is well-aligned with the PR's objective of supporting the atmos.d directory convention. This package follows the XDG Base Directory Specification, making it perfect for managing configuration file locations.pkg/config/home.go (4)
16-21
: Well-structured cache implementation!Good use of RWMutex for thread-safe access to the cache, and clear documentation for the exported DisableCache variable.
56-78
: Solid path expansion implementation!Good input validation and safe path handling using filepath.Join.
80-88
: Clean and focused Reset implementation!Good documentation explaining the test use case and proper lock handling.
1-168
: Strong implementation with comprehensive OS support!The home directory detection implementation is well-structured with:
- Robust thread-safety using RWMutex
- Comprehensive OS support with proper fallbacks
- Clear documentation and error handling
Consider the suggested improvements for even better maintainability and reliability.
pkg/config/con.go (1)
189-189
: Possible redundant slice initializationAt line 189, the slice
configPaths
is appended withpaths...
inside a loop. Ensure this is intended and does not lead to redundant entries.Please verify that
configPaths
does not accumulate duplicate paths when processing directories.website/docs/cli/configuration/configuration.mdx (3)
27-29
: LGTM! Clear explanation of configuration processingThe three-stage configuration processing explanation provides a clear mental model for users to understand how Atmos loads and merges configurations. The explanation about
ATMOS_CLI_CONFIG_PATH
environment variable updates is particularly helpful for understanding cross-platform compatibility.
149-185
: LGTM! Comprehensive imports documentation with security considerationsThe imports section effectively covers:
- Clear explanation of import functionality and deep-merge behavior
- Well-structured list of import types
- Important security considerations for remote imports
- Clear warning about templated imports not being supported
🧰 Tools
🪛 LanguageTool
[grammar] ~181-~181: Did you mean “is” or “can be”?
Context: ... supported (unlike stacks). :::warning Be Careful with Remote Imports - Always us...(BE_IS)
189-215
: LGTM! Clear and comprehensive base path documentationThe base path section effectively explains:
- The relationship between base path and configuration loading
- Clear precedence rules for path resolution
- Detailed explanation of how relative and absolute paths are handled
- Well-structured explanation of path inference strategy
pkg/config/ReadMe.md (4)
1-42
: Well structured flowchart documenting the configuration loading process!The flowchart clearly illustrates the recursive configuration loading process, including the new support for atmos.d directory imports. The styling helps distinguish between different types of operations.
49-138
: Excellent documentation of the configuration discovery process!The flowchart comprehensively documents the three-stage configuration loading process, clearly showing how atmos.d directories are supported in multiple locations with proper fallback mechanisms.
139-190
: Clear documentation of base path resolution logic!The flowchart effectively illustrates the precedence order and fallback mechanisms for base path computation, including the new support for atmos.d directories in path inference.
45-48
:⚠️ Potential issueFix YAML frontmatter syntax in Mermaid diagram.
The YAML frontmatter section should use three dashes for both opening and closing.
-config: - layout: fixed +--- +config: + layout: fixed +---Likely invalid or redundant comment.
pkg/config/atmos.yaml (6)
11-18
: Well documented base path configuration!The configuration clearly documents the base path handling, including support for both absolute and relative paths, with proper documentation of environment variables and CLI arguments.
20-29
: Solid vendor configuration documentation!The configuration effectively documents the vendor path handling, including support for directory-based configurations with clear ordering rules.
45-46
: Verify security implications of terraform auto-approve.While the
apply_auto_approve
flag is disabled by default (which is good), please ensure that proper safeguards are in place when enabling this flag, as it bypasses manual verification of terraform plans.Consider adding a comment warning about the security implications of enabling this flag.
67-79
: Excellent stack configuration with proper glob support!The stack configuration properly documents the new atmos.d support with glob patterns for included and excluded paths.
263-287
: Well structured validation schema configuration!The configuration properly documents multiple schema validation options (JSON Schema, OPA, Atmos manifest) with clear path configurations and helpful reference links.
305-316
: Important list merge strategy documentation!The configuration clearly documents the list merge strategies, which is crucial for the new atmos.d support where multiple configuration files may need to be merged.
errorMessage := fmt.Sprintf("\nno stack manifests found in the provided "+ | ||
"paths:\n%s\n\nCheck if `base_path`, 'stacks.base_path', 'stacks.included_paths' and 'stacks.excluded_paths' are correctly set in CLI config "+ | ||
"files or ENV vars.", j) | ||
return fmt.Errorf(errorMessage) |
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.
Potential issue with dynamic format string in fmt.Errorf
At line 660, fmt.Errorf(errorMessage)
is called with a dynamic string errorMessage
. This can lead to unexpected behavior if errorMessage
contains formatting verbs. Consider passing errorMessage
as an argument instead.
Apply this change to fix the issue:
-return fmt.Errorf(errorMessage)
+return errors.New(errorMessage)
📝 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.
return fmt.Errorf(errorMessage) | |
return errors.New(errorMessage) |
🧰 Tools
🪛 golangci-lint (1.62.2)
660-660: printf: non-constant format string in call to fmt.Errorf
(govet)
if existingFile, exists := priorityMap[key]; exists { | ||
if ext == ".yaml" { | ||
priorityMap[key] = file // Replace .yml with .yaml | ||
} else if ext == ".yml" && strings.HasSuffix(existingFile, ".yaml") { |
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.
Unused HasSuffix
result
At line 773, the result of strings.HasSuffix(existingFile, ".yaml")
is computed but not used. This may indicate a logical error or unnecessary computation.
If the intention was to check the suffix, consider using the result in a condition.
-} else if ext == ".yml" && strings.HasSuffix(existingFile, ".yaml") {
+} else if ext == ".yml" && strings.HasSuffix(existingFile, ".yaml") {
+ // Handle the case when existingFile ends with .yaml
+}
Committable suggestion skipped: line range outside the PR's diff.
🧰 Tools
🪛 golangci-lint (1.62.2)
773-773: SA4017: HasSuffix doesn't have side effects and its return value is ignored
(staticcheck)
defer func(reader *os.File) { | ||
err := reader.Close() | ||
if err != nil { | ||
u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '"+path+"'. "+err.Error())) |
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.
Potential issue with dynamic format string in fmt.Sprintf
At line 838, fmt.Sprintf
is used with a dynamic string, which might contain unexpected formatting verbs. This can lead to runtime errors.
Change the fmt.Sprintf
statement to concatenate strings safely.
-u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '"+path+"'. "+err.Error()))
+u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '%s'. %v", path, err))
📝 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.
u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '"+path+"'. "+err.Error())) | |
u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '%s'. %v", path, err)) |
🧰 Tools
🪛 golangci-lint (1.62.2)
838-838: printf: non-constant format string in call to fmt.Sprintf
(govet)
// Create a mock HTTP server | ||
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { | ||
w.WriteHeader(http.StatusOK) | ||
w.Write([]byte("mock content")) |
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.
🛠️ Refactor suggestion
Unchecked error from w.Write
At line 244, the return value of w.Write([]byte("mock content"))
is not checked. Ignoring errors can lead to unnoticed failures.
Check the error returned by w.Write
to ensure the content is written successfully.
-w.Write([]byte("mock content"))
+if _, err := w.Write([]byte("mock content")); err != nil {
+ t.Fatalf("Failed to write mock content: %v", err)
+}
📝 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.
w.Write([]byte("mock content")) | |
if _, err := w.Write([]byte("mock content")); err != nil { | |
t.Fatalf("Failed to write mock content: %v", err) | |
} |
🧰 Tools
🪛 golangci-lint (1.62.2)
244-244: Error return value of w.Write
is not checked
(errcheck)
func downloadRemoteConfig(url string) (string, string, error) { | ||
tempDir, err := os.MkdirTemp("", "atmos-import-*") | ||
if err != nil { | ||
return "", "", err | ||
} | ||
tempFile := filepath.Join(tempDir, "atmos.yaml") | ||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) | ||
defer cancel() | ||
client := &getter.Client{ | ||
Ctx: ctx, | ||
Src: url, | ||
Dst: tempFile, | ||
Mode: getter.ClientModeFile, | ||
} | ||
err = client.Get() | ||
if err != nil { | ||
os.RemoveAll(tempDir) | ||
return "", "", fmt.Errorf("failed to download remote config: %w", err) |
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.
I'm concerned if we're repeating calls to GoGetter, since we have another implementation that properly handles GITHUB_TOKEN
. Do not want that logic distributed.
pkg/config/home.go
Outdated
var stdout bytes.Buffer | ||
|
||
// If that fails, try OS specific commands | ||
if runtime.GOOS == "darwin" { | ||
cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`) | ||
cmd.Stdout = &stdout | ||
if err := cmd.Run(); err == nil { | ||
result := strings.TrimSpace(stdout.String()) | ||
if result != "" { | ||
return result, nil | ||
} | ||
} | ||
} else { | ||
cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid())) | ||
cmd.Stdout = &stdout |
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.
It seems like this file is copying another wholesale, without copyright or attribution. I suggest we copy the deprecated package into it's own folder, including the LICENSE
file in that folder. Do not mingle the code.
@haitham911 please resolve the conflicts |
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: 6
♻️ Duplicate comments (3)
pkg/config/con.go (3)
687-687
:⚠️ Potential issueAvoid using dynamic format strings with
fmt.Errorf
At line 687, using
fmt.Errorf(errorMessage)
with a dynamic string can cause unexpected behavior. It's safer to useerrors.New(errorMessage)
instead.Suggested fix:
-return fmt.Errorf(errorMessage) +return errors.New(errorMessage)🧰 Tools
🪛 golangci-lint (1.62.2)
687-687: printf: non-constant format string in call to fmt.Errorf
(govet)
800-800
: 🛠️ Refactor suggestionEnsure the result of
strings.HasSuffix
is usedAt line 800, the result of
strings.HasSuffix(existingFile, ".yaml")
isn't effectively used. Let's verify the condition to ensure it's working as intended.🧰 Tools
🪛 golangci-lint (1.62.2)
800-800: SA4017: HasSuffix doesn't have side effects and its return value is ignored
(staticcheck)
865-865
:⚠️ Potential issueAvoid using dynamic format strings with
fmt.Sprintf
At line 865, using
fmt.Sprintf
with a dynamic format string might lead to runtime errors ifpath
orerr.Error()
contain formatting verbs. Let's use format specifiers safely.Suggested fix:
-u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '"+path+"'. "+err.Error())) +u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '%s'. %v", path, err))🧰 Tools
🪛 golangci-lint (1.62.2)
865-865: printf: non-constant format string in call to fmt.Sprintf
(govet)
🧹 Nitpick comments (10)
pkg/config/base-path.go (1)
3-3
: Add proper function documentation.The function lacks proper documentation. Let's add a clear description of its purpose, parameters, and return values.
+// BasePathComputing computes the base path for Atmos configurations. +// It checks for the --base-path CLI argument and updates ATMOS_BASE_PATH environment variable. +// If a relative path is provided, it is converted to an absolute path. +// Returns the computed base path or an empty string if no base path is set or on error. func (cl *ConfigLoader) BasePathComputing() string {pkg/config/imports.go (1)
73-75
: Consider enhancingisRemoteImport
function for robustness.Using
url.Parse
and checking the scheme inisRemoteImport
can make the function more robust against different URL formats and edge cases.Apply this diff:
func isRemoteImport(importPath string) bool { - return strings.HasPrefix(importPath, "http://") || strings.HasPrefix(importPath, "https://") + parsedURL, err := url.Parse(strings.TrimSpace(importPath)) + if err != nil { + return false + } + return parsedURL.Scheme == "http" || parsedURL.Scheme == "https" }pkg/config/imports_test.go (2)
76-76
: Use a unique temporary directory fortempDir
in tests.Currently, the test uses
"./test"
as thetempDir
, which can cause conflicts in concurrent test runs. It's better to use a unique temporary directory.Apply this diff:
- resolvedPaths, err := configLoader.processImports(imports, "./test", 0, 10) + resolvedPaths, err := configLoader.processImports(imports, t.TempDir(), 0, 10)
134-136
: Prefert.TempDir()
overos.MkdirTemp
in tests.Using
t.TempDir()
simplifies temporary directory management in tests and ensures cleanup. Replaceos.MkdirTemp
and manual cleanup witht.TempDir()
.Apply this diff:
- tempDir, err := os.MkdirTemp("", "config-test") - assert.NoError(t, err) - defer os.RemoveAll(tempDir) + tempDir := t.TempDir()Also applies to: 144-146, 154-156
pkg/config/homedir/home.go (2)
118-121
: Useerrors.Is
for error comparisonWhen checking errors from
cmd.Run()
, directly comparingerr
toexec.ErrNotFound
might not handle wrapped errors correctly. Usingerrors.Is(err, exec.ErrNotFound)
ensures proper error handling.
164-164
: Update error message to match variables checkedThe error message mentions
USERPROFILE
, but onlyHOMEDRIVE
andHOMEPATH
are checked here. Let's adjust the message to reflect this accurately.pkg/config/con.go (1)
453-454
: Remove duplicate commentThe comment for
applyUserPreferences
is duplicated. Removing the redundant line will keep the code clean.pkg/config/con_test.go (3)
151-186
: Consider splitting this test function for better maintainability.This test function is handling multiple test cases (single file, multiple files, directory). Consider splitting it into separate test functions for better clarity:
TestLoadExplicitConfigsWithSingleFile
TestLoadExplicitConfigsWithMultipleFiles
TestLoadExplicitConfigsFromDirectory
61-101
: Consider adding more edge cases to the config file search test.The test could be enhanced with additional cases:
- Empty directory
- Directory with unsupported file extensions
- Directory with subdirectories containing config files
19-271
: Strong test coverage with room for enhancement!The test suite provides comprehensive coverage of the configuration loading functionality. Consider:
- Adding more edge cases for error scenarios
- Testing concurrent access to configuration
- Adding benchmarks for performance-critical operations
Would you like me to help generate additional test cases or benchmarks?
🧰 Tools
🪛 golangci-lint (1.62.2)
244-244: Error return value of
w.Write
is not checked(errcheck)
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (8)
pkg/config/base-path.go
(1 hunks)pkg/config/con.go
(1 hunks)pkg/config/con_test.go
(1 hunks)pkg/config/homedir/LICENSE
(1 hunks)pkg/config/homedir/home.go
(1 hunks)pkg/config/homedir/home_test.go
(1 hunks)pkg/config/imports.go
(1 hunks)pkg/config/imports_test.go
(1 hunks)
✅ Files skipped from review due to trivial changes (1)
- pkg/config/homedir/LICENSE
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/config/imports_test.go
161-161: ineffectual assignment to err
(ineffassign)
pkg/config/con.go
542-542: Error return value of cl.deepMergeConfig
is not checked
(errcheck)
687-687: printf: non-constant format string in call to fmt.Errorf
(govet)
865-865: printf: non-constant format string in call to fmt.Sprintf
(govet)
800-800: SA4017: HasSuffix doesn't have side effects and its return value is ignored
(staticcheck)
pkg/config/con_test.go
244-244: Error return value of w.Write
is not checked
(errcheck)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Summary
🔇 Additional comments (4)
pkg/config/base-path.go (1)
3-10
: Add unit tests for the new functionality.Ensure comprehensive test coverage for the BasePathComputing method.
Create a test file with cases for:
- Valid absolute path
- Valid relative path
- Invalid path
- Empty path
- Environment variable setting
- Error conditions
Would you like me to generate a comprehensive test suite for this functionality?
pkg/config/homedir/home_test.go (1)
1-129
: Tests look solidThe test cases cover various scenarios effectively.
pkg/config/con_test.go (2)
1-17
: Clean and well-organized imports!The package declaration and imports are properly structured and follow Go conventions.
244-244
: Unchecked error fromw.Write
The return value of
w.Write([]byte("mock content"))
should be checked for errors.🧰 Tools
🪛 golangci-lint (1.62.2)
244-244: Error return value of
w.Write
is not checked(errcheck)
func (cl *ConfigLoader) BasePathComputing() string { | ||
// Is --base-path Provided? | ||
// If --base-path arg is provided | ||
// Yes --> "Set base_path to --base-path (resolve absolute if relative)" | ||
// Update ATMOS_BASE_PATH with absolute path | ||
|
||
return "" | ||
} |
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.
Implementation needed: BasePathComputing is currently a stub.
Greetings, warrior! The current implementation returns an empty string and doesn't fulfill the requirements outlined in the comments. Here's what needs to be implemented:
- Parse and validate the --base-path CLI argument
- Convert relative paths to absolute paths
- Update the ATMOS_BASE_PATH environment variable
- Return the computed base path
Here's a suggested implementation structure:
+import (
+ "os"
+ "path/filepath"
+)
+
func (cl *ConfigLoader) BasePathComputing() string {
- // Is --base-path Provided?
- // If --base-path arg is provided
- // Yes --> "Set base_path to --base-path (resolve absolute if relative)"
- // Update ATMOS_BASE_PATH with absolute path
+ if basePath := cl.getBasePathFromCLI(); basePath != "" {
+ absPath, err := filepath.Abs(basePath)
+ if err != nil {
+ cl.logger.Error("Failed to resolve absolute path", "error", err)
+ return ""
+ }
+
+ if err := os.Setenv("ATMOS_BASE_PATH", absPath); err != nil {
+ cl.logger.Error("Failed to set ATMOS_BASE_PATH", "error", err)
+ return ""
+ }
+
+ return absPath
+ }
return ""
}
Committable suggestion skipped: line range outside the PR's diff.
🛠️ Refactor suggestion
Add error handling and validation.
The function should include proper error handling for various scenarios:
- Invalid paths
- Permission issues
- Environment variable setting failures
Consider returning an error along with the string to properly handle and communicate failures:
-func (cl *ConfigLoader) BasePathComputing() string {
+func (cl *ConfigLoader) BasePathComputing() (string, error) {
if basePath := cl.getBasePathFromCLI(); basePath != "" {
absPath, err := filepath.Abs(basePath)
if err != nil {
- cl.logger.Error("Failed to resolve absolute path", "error", err)
- return ""
+ return "", fmt.Errorf("failed to resolve absolute path: %w", err)
}
+ if _, err := os.Stat(absPath); err != nil {
+ return "", fmt.Errorf("invalid base path: %w", err)
+ }
+
if err := os.Setenv("ATMOS_BASE_PATH", absPath); err != nil {
- cl.logger.Error("Failed to set ATMOS_BASE_PATH", "error", err)
- return ""
+ return "", fmt.Errorf("failed to set ATMOS_BASE_PATH: %w", err)
}
- return absPath
+ return absPath, nil
}
- return ""
+ return "", nil
}
Committable suggestion skipped: line range outside the PR's diff.
pkg/config/imports.go
Outdated
} | ||
err := client.Get() | ||
if err != nil { | ||
os.RemoveAll(tempDir) |
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.
Prevent unintended deletion of temporary files on download failure.
When a download error occurs in downloadRemoteConfig
, the function removes the entire tempDir
, which may contain other configuration files. This could lead to data loss if multiple remote imports are being processed. It's safer to remove only the temporary file associated with the failed download.
Apply this diff to fix the issue:
- os.RemoveAll(tempDir)
+ os.Remove(tempFile)
Committable suggestion skipped: line range outside the PR's diff.
"local.yaml", | ||
server.URL + "/config.yaml", | ||
} | ||
resolved, err := cl.processImports(importPaths, tempDir, 11, 10) |
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.
Handle the error returned by processImports
in the test.
In TestProcessImportNested
, the error returned by processImports
when the max depth is exceeded is not checked. To properly validate the expected behavior, the test should assert that an error is returned.
Apply this diff:
resolved, err := cl.processImports(importPaths, tempDir, 11, 10)
- assert.Nil(t, resolved, "no resolved paths should be returned on depth limit breach")
+ assert.Error(t, err, "should return an error when maxDepth is exceeded")
+ assert.Nil(t, resolved, "no resolved paths should be returned on depth limit breach")
📝 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.
resolved, err := cl.processImports(importPaths, tempDir, 11, 10) | |
resolved, err := cl.processImports(importPaths, tempDir, 11, 10) | |
assert.Error(t, err, "should return an error when maxDepth is exceeded") | |
assert.Nil(t, resolved, "no resolved paths should be returned on depth limit breach") |
🧰 Tools
🪛 golangci-lint (1.62.2)
161-161: ineffectual assignment to err
(ineffassign)
func Dir() (string, error) { | ||
if !DisableCache { | ||
cacheLock.RLock() | ||
cached := homedirCache | ||
cacheLock.RUnlock() | ||
if cached != "" { | ||
return cached, nil | ||
} | ||
} | ||
|
||
cacheLock.Lock() | ||
defer cacheLock.Unlock() | ||
|
||
var result string | ||
var err error | ||
if runtime.GOOS == "windows" { | ||
result, err = dirWindows() | ||
} else { | ||
// Unix-like system, so just assume Unix | ||
result, err = dirUnix() | ||
} | ||
|
||
if err != nil { | ||
return "", err | ||
} | ||
homedirCache = result | ||
return result, nil | ||
} |
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.
🛠️ Refactor suggestion
Avoid updating the cache when caching is disabled
Even when DisableCache
is true, Dir()
updates homedirCache
, which could lead to unintended caching. Let's guard the cache update with if !DisableCache
to ensure proper behavior.
if found { | ||
u.LogTrace(cl.atmosConfig, fmt.Sprintf("merge import paths: %v", resolvedPaths)) | ||
} | ||
cl.deepMergeConfig() |
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.
Check the error returned by cl.deepMergeConfig()
At line 542, the error from cl.deepMergeConfig()
isn't checked. This might hide issues if the merge fails. Let's handle the error to catch any problems.
Suggested fix:
- cl.deepMergeConfig()
+ if err := cl.deepMergeConfig(); err != nil {
+ return err
+ }
📝 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.
cl.deepMergeConfig() | |
if err := cl.deepMergeConfig(); err != nil { | |
return err | |
} |
🧰 Tools
🪛 golangci-lint (1.62.2)
542-542: Error return value of cl.deepMergeConfig
is not checked
(errcheck)
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: 1
🧹 Nitpick comments (9)
pkg/config/imports.go (3)
18-36
: Validate URL scheme before download attempt.The function should validate the URL scheme before attempting the download. This would prevent unnecessary network calls for invalid URLs.
func (cl *ConfigLoader) downloadRemoteConfig(url string, tempDir string) (string, error) { + parsedURL, err := url.Parse(url) + if err != nil || (parsedURL.Scheme != "http" && parsedURL.Scheme != "https") { + return "", fmt.Errorf("invalid URL scheme '%s': %v", url, err) + } // uniq name for the temp file fileName := fmt.Sprintf("atmos-%d.yaml", time.Now().UnixNano())
37-40
: Add directory existence check.The function should verify that tempDir exists before proceeding. This would prevent cryptic errors later in the process.
if tempDir == "" { return nil, fmt.Errorf("tempDir required to process imports") } +if _, err := os.Stat(tempDir); os.IsNotExist(err) { + return nil, fmt.Errorf("tempDir '%s' does not exist", tempDir) +}
89-91
: Add basic YAML validation before parsing.Consider adding basic YAML validation before attempting to parse the configuration to provide more helpful error messages.
v := viper.New() v.SetConfigType("yaml") +// Basic YAML validation +content, err := os.ReadFile(tempFile) +if err != nil { + return nil, fmt.Errorf("failed to read config file '%s': %v", tempFile, err) +} +if len(content) == 0 { + return nil, fmt.Errorf("config file '%s' is empty", tempFile) +} found, _, err := cl.loadConfigFile(cl.atmosConfig, tempFile, v)pkg/config/con.go (6)
46-134
: Consider adding flowchart documentation.The
LoadConfig
method implements a well-structured multi-stage configuration loading process. Consider adding a link to the flowchart documentation in the method's comments to help future maintainers understand the loading stages and their relationships.
684-688
: Use errors.New for static error messages.Replace
fmt.Errorf
witherrors.New
when the error message is pre-formatted and doesn't require any formatting verbs.Apply this change:
- return fmt.Errorf(errorMessage) + return errors.New(errorMessage)🧰 Tools
🪛 golangci-lint (1.62.2)
687-687: printf: non-constant format string in call to fmt.Errorf
(govet)
862-867
: Improve error message formatting.Use proper format verbs instead of string concatenation for better error message handling.
Apply this change:
- u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '"+path+"'. "+err.Error())) + u.LogWarning(atmosConfig, fmt.Sprintf("error closing file '%s': %v", path, err))🧰 Tools
🪛 golangci-lint (1.62.2)
865-865: printf: non-constant format string in call to fmt.Sprintf
(govet)
800-802
: Remove redundant HasSuffix check.The
HasSuffix
check is unnecessary as the condition is already handled by the extension comparison.Apply this change:
- } else if ext == ".yml" && strings.HasSuffix(existingFile, ".yaml") { + } else if ext == ".yml" { continue // Keep .yaml priority }🧰 Tools
🪛 golangci-lint (1.62.2)
800-800: SA4017: HasSuffix doesn't have side effects and its return value is ignored
(staticcheck)
299-301
: Enhance error context in error wrapping.Consider adding more context when wrapping errors to help with debugging. Include relevant values that led to the error.
Apply this change:
- return fmt.Errorf("error loading configs from ATMOS_CLI_CONFIG_PATH: %w", err) + return fmt.Errorf("error loading configs from ATMOS_CLI_CONFIG_PATH '%s': %w", cl.atmosConfig.CliConfigPath, err)
523-523
: Use more specific temporary directory prefix.Consider using a more specific prefix for the temporary directory to better identify its purpose.
Apply this change:
- tempDir, err := os.MkdirTemp("", "atmos-import-*") + tempDir, err := os.MkdirTemp("", "atmos-config-import-*")
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
pkg/config/con.go
(1 hunks)pkg/config/imports.go
(1 hunks)
🧰 Additional context used
🪛 golangci-lint (1.62.2)
pkg/config/con.go
542-542: Error return value of cl.deepMergeConfig
is not checked
(errcheck)
687-687: printf: non-constant format string in call to fmt.Errorf
(govet)
865-865: printf: non-constant format string in call to fmt.Sprintf
(govet)
800-800: SA4017: HasSuffix doesn't have side effects and its return value is ignored
(staticcheck)
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Summary
🔇 Additional comments (3)
pkg/config/imports.go (1)
72-75
: Clean and focused implementation!The helper function is well-scoped and handles the common HTTP(S) schemes appropriately.
pkg/config/con.go (2)
22-34
: Well-structured type declarations!The
MaximumImportLvL
constant effectively prevents infinite import loops, and theConfigLoader
struct is cleanly organized with clear field purposes.
542-542
:⚠️ Potential issueCheck error from deepMergeConfig.
The error returned by
deepMergeConfig()
should be checked to ensure proper error propagation.Apply this fix:
- cl.deepMergeConfig() + if err := cl.deepMergeConfig(); err != nil { + return err + }Likely invalid or redundant comment.
🧰 Tools
🪛 golangci-lint (1.62.2)
542-542: Error return value of
cl.deepMergeConfig
is not checked(errcheck)
localPath := filepath.Join(basePath, importPath) | ||
paths, err := cl.SearchAtmosConfigFileDir(localPath) |
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.
Add path traversal protection.
The function should ensure that the resolved path doesn't escape the base directory through path traversal.
localPath := filepath.Join(basePath, importPath)
+// Ensure the resolved path is still under basePath
+if !strings.HasPrefix(filepath.Clean(localPath), filepath.Clean(basePath)) {
+ return nil, fmt.Errorf("import path '%s' attempts to escape base directory", importPath)
+}
paths, err := cl.SearchAtmosConfigFileDir(localPath)
📝 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.
localPath := filepath.Join(basePath, importPath) | |
paths, err := cl.SearchAtmosConfigFileDir(localPath) | |
localPath := filepath.Join(basePath, importPath) | |
// Ensure the resolved path is still under basePath | |
if !strings.HasPrefix(filepath.Clean(localPath), filepath.Clean(basePath)) { | |
return nil, fmt.Errorf("import path '%s' attempts to escape base directory", importPath) | |
} | |
paths, err := cl.SearchAtmosConfigFileDir(localPath) |
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: 0
🧹 Nitpick comments (5)
pkg/config/config.go (3)
280-306
: Consider consolidating error handling for directory checks.The implementation correctly handles both
atmos.d
and.atmos.d
directories with proper path traversal protection. However, the error handling pattern is repeated.Consider consolidating the directory checks:
- atmosDPath := filepath.Join(basePath, "atmos.d") - if !strings.HasPrefix(atmosDPath, basePath) { - u.LogWarning(atmosConfig, "invalid atmos.d path: attempted directory traversal") - } - _, err = os.Stat(atmosDPath) - if err == nil { - atmosConfig.Import = []string{"atmos.d/**/*.yaml", "atmos.d/**/*.yml"} - } else if !os.IsNotExist(err) { - return atmosConfig, err - } - atmosDPath = filepath.Join(atmosDPath, ".atmos.d") - _, err = os.Stat(atmosDPath) - if err == nil { - cliImport := []string{".atmos.d/**/*.yaml", ".atmos.d/**/*.yml"} - atmosConfig.Import = append(atmosConfig.Import, cliImport...) - } else if !os.IsNotExist(err) { - return atmosConfig, err - } + for _, dir := range []string{"atmos.d", ".atmos.d"} { + atmosDPath := filepath.Join(basePath, dir) + if !strings.HasPrefix(atmosDPath, basePath) { + u.LogWarning(atmosConfig, fmt.Sprintf("invalid %s path: attempted directory traversal", dir)) + continue + } + _, err = os.Stat(atmosDPath) + if err == nil { + patterns := []string{fmt.Sprintf("%s/**/*.yaml", dir), fmt.Sprintf("%s/**/*.yml", dir)} + if atmosConfig.Import == nil { + atmosConfig.Import = patterns + } else { + atmosConfig.Import = append(atmosConfig.Import, patterns...) + } + } else if !os.IsNotExist(err) { + return atmosConfig, err + } + }
465-537
: Consider adding rate limiting for remote imports.The import processing implementation is robust with proper URL validation and temporary file cleanup. However, when processing multiple remote imports, there's no rate limiting mechanism in place.
Consider adding rate limiting:
+var importLimiter = make(chan struct{}, 3) // Allow 3 concurrent downloads + func processImports(atmosConfig schema.AtmosConfiguration, v *viper.Viper) error { + defer func() { + // Cleanup any remaining tokens + for len(importLimiter) > 0 { + <-importLimiter + } + }() + for _, importPath := range atmosConfig.Import { if importPath == "" { continue } if strings.HasPrefix(importPath, "http://") || strings.HasPrefix(importPath, "https://") { + importLimiter <- struct{}{} // Acquire token tempDir, tempFile, err := downloadRemoteConfig(importPath) + <-importLimiter // Release token
539-558
: Consider enhancing logging for remote downloads.The remote config downloading implementation is secure with proper timeout handling and cleanup. However, it could benefit from additional logging to help with troubleshooting.
Consider adding debug logging:
func downloadRemoteConfig(url string) (string, string, error) { + u.LogDebug(fmt.Sprintf("Downloading remote config from %s", url)) tempDir, err := os.MkdirTemp("", "atmos-import-*") if err != nil { return "", "", err } tempFile := filepath.Join(tempDir, "atmos.yaml") + u.LogDebug(fmt.Sprintf("Using temporary file %s", tempFile)) ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() client := &getter.Client{ Ctx: ctx, Src: url, Dst: tempFile, Mode: getter.ClientModeFile, } err = client.Get() if err != nil { + u.LogDebug(fmt.Sprintf("Failed to download from %s: %v", url, err)) os.RemoveAll(tempDir) return "", "", fmt.Errorf("failed to download remote config: %w", err) } + u.LogDebug(fmt.Sprintf("Successfully downloaded config from %s", url)) return tempDir, tempFile, nil }pkg/config/utils.go (2)
787-830
: Consider enhancing error messages for better debugging.The ProcessConfigFile implementation is robust with proper handling of both single files and directories. However, the error messages could be more descriptive.
Consider enhancing error messages:
func ProcessConfigFile(config interface{}, configPath string, v *viper.Viper) (bool, string, error) { absPath, err := filepath.Abs(configPath) if err != nil { - return false, "", fmt.Errorf("unable to get absolute path for %s: %w", configPath, err) + return false, "", fmt.Errorf("failed to resolve absolute path for config file %s: %w", configPath, err) } info, err := os.Stat(absPath) if os.IsNotExist(err) { return false, "", nil } else if err != nil { - return false, "", fmt.Errorf("error accessing config file (%s): %w", absPath, err) + return false, "", fmt.Errorf("failed to access config file %s (permissions or I/O error): %w", absPath, err) }
832-843
: Consider adding directory existence validation.The GetHomeDir implementation correctly retrieves the user's home directory with proper error handling. However, it could benefit from additional validation.
Consider adding directory validation:
func GetHomeDir() (string, error) { homeDir, err := os.UserHomeDir() if err != nil { return "", fmt.Errorf("unable to determine home directory: %w", err) } if homeDir == "" { return "", fmt.Errorf("home directory not found") } + // Validate that the directory exists and is accessible + info, err := os.Stat(homeDir) + if err != nil { + return "", fmt.Errorf("home directory %s is not accessible: %w", homeDir, err) + } + if !info.IsDir() { + return "", fmt.Errorf("home directory %s is not a directory", homeDir) + } return homeDir, nil }
📜 Review details
Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
go.sum
is excluded by!**/*.sum
📒 Files selected for processing (8)
.github/workflows/test.yml
(1 hunks)go.mod
(2 hunks)internal/exec/helmfile.go
(1 hunks)internal/exec/terraform.go
(1 hunks)pkg/config/config.go
(6 hunks)pkg/config/utils.go
(2 hunks)pkg/schema/schema.go
(1 hunks)pkg/utils/glob_utils.go
(2 hunks)
🚧 Files skipped from review as they are similar to previous changes (6)
- .github/workflows/test.yml
- go.mod
- pkg/utils/glob_utils.go
- internal/exec/terraform.go
- pkg/schema/schema.go
- internal/exec/helmfile.go
🧰 Additional context used
📓 Learnings (1)
pkg/config/config.go (2)
Learnt from: osterman
PR: cloudposse/atmos#808
File: pkg/config/config.go:478-483
Timestamp: 2024-12-02T21:26:32.337Z
Learning: In the 'atmos' project, when reviewing Go code like `pkg/config/config.go`, avoid suggesting file size checks after downloading remote configs if such checks aren't implemented elsewhere in the codebase.
Learnt from: osterman
PR: cloudposse/atmos#887
File: internal/exec/stack_processor_utils.go:380-380
Timestamp: 2024-12-25T20:28:47.526Z
Learning: Windows path handling often requires `filepath.Join` to ensure correct separators and comparisons. Insufficient tests can break cross-platform compatibility, so migrating from `path.Join` to `filepath.Join` needs thorough testing on Windows before merging.
⏰ Context from checks skipped due to timeout of 90000ms (1)
- GitHub Check: Summary
🔇 Additional comments (1)
pkg/config/config.go (1)
144-158
: LGTM! Clean implementation of configuration loading hierarchy.The configuration loading logic follows a clear precedence order and properly handles the
ATMOS_CLI_CONFIG_PATH
environment variable. The error handling is thorough and the early return pattern is well applied.
what
atmos.d
convention foratmos.yaml
configuration, allowing automatic inclusion of configuration files from the atmos.d directory.import
key inside `atmos.yaml, allowing users to define a list of locations (local files, directories using glob patterns, and remote URLs) to import configurations from.ATMOS_CLI_CONFIG_PATH
andATMOS_BASE_PATH
before running terraform and helm cmdexamples/demo-env
that shows exposed env variablesexamples/demo-atmos-cli-imports
for custom inclusion of configuration files from the atmos.d directoryexamples/demo-atmos.d
for automatic inclusion of configuration files from the atmos.d directorywhy
Simplifies configuration management, enabling overrides at multiple levels of processing, making it easier to include additional configurations without explicit declarations.
Details
Load Config
3 Stages
Base Path
references
Summary by CodeRabbit
Release Notes
New Features
Configuration Improvements
ATMOS_CLI_CONFIG_PATH
andATMOS_BASE_PATH
environment variables.Bug Fixes
Documentation
Performance