Skip to content
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

Enhance Atmos Configuration with atmos.d Support and Import Functionality #1085

Open
wants to merge 49 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
7f13ce6
refactor InitCliConfig
haitham911 Feb 17, 2025
9ed97f7
add readConfig function
haitham911 Feb 18, 2025
5389ffc
Merge branch 'main' into Viper-built-in-configuration
haitham911 Feb 18, 2025
1fe3e66
add load config
haitham911 Feb 18, 2025
1cfec36
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 18, 2025
3ad2d48
add default.go & update snapshots
haitham911 Feb 18, 2025
263d14a
resolve comments
haitham911 Feb 18, 2025
2532cfc
remove unused function processConfigFile
haitham911 Feb 19, 2025
86bf3cc
improve log
haitham911 Feb 19, 2025
9de9d7b
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 19, 2025
037560c
add imports
haitham911 Feb 21, 2025
5e134f5
update snapshots
haitham911 Feb 21, 2025
413f955
add import config test
haitham911 Feb 21, 2025
eae3f97
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 21, 2025
6c2f103
refactor LoadConfig into smaller helper functions
haitham911 Feb 21, 2025
0bc7b4a
update Valid_Log_Level snapshots
haitham911 Feb 21, 2025
f87f1e9
update log snapshots
haitham911 Feb 21, 2025
d660739
update snapshots
haitham911 Feb 22, 2025
1858c2b
update snapshots Valid_Log_Level
haitham911 Feb 22, 2025
36cd759
Merge branch 'main' into atmos-import-config
haitham911 Feb 22, 2025
daaa03c
remove //nolint:revive
haitham911 Feb 22, 2025
c0db066
Exclude any line containing "log."
haitham911 Feb 22, 2025
8920e56
fix lint revive.add-constant
haitham911 Feb 22, 2025
09fe32d
update add-constant to include "error","path"
haitham911 Feb 22, 2025
e4b6f24
improve code lint
haitham911 Feb 22, 2025
da8006a
improve log
haitham911 Feb 22, 2025
54bb994
improve log charmbracelet aliased
haitham911 Feb 23, 2025
36ab8e8
Preprocess Atmos config with custom functions
haitham911 Feb 24, 2025
e42aa94
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 24, 2025
19136b0
remove print line
haitham911 Feb 24, 2025
0c95ad9
improve logs
haitham911 Feb 24, 2025
b2669c7
refactor processScalarNode
haitham911 Feb 24, 2025
ab18aab
improve logs
haitham911 Feb 24, 2025
0a9353a
[autofix.ci] apply automated fixes
autofix-ci[bot] Feb 24, 2025
462b5bf
fix linter error
haitham911 Feb 24, 2025
55a9b05
fix linter errors
haitham911 Feb 24, 2025
115006b
update snapshots
haitham911 Feb 24, 2025
6180ff5
fix linter errors
haitham911 Feb 24, 2025
6f5d329
fix linter
haitham911 Feb 24, 2025
1cb9623
disable atmos_vendor_pull
haitham911 Feb 25, 2025
d4f7630
update snapshots Valid_Log_Level_in_Environment_Variable
haitham911 Feb 25, 2025
ea71b2f
update snapshots
haitham911 Feb 25, 2025
7cda8a4
Merge branch 'main' into atmos-import-config
haitham911 Mar 4, 2025
2ee18b9
set config path dir
haitham911 Mar 4, 2025
25688c6
update snapshots
haitham911 Mar 5, 2025
351c73b
add processStackConfigs on config.go
haitham911 Mar 5, 2025
9ab9d3c
[autofix.ci] apply automated fixes
autofix-ci[bot] Mar 5, 2025
eff1fd7
fix linter errors config.go
haitham911 Mar 9, 2025
6c661ac
add nolint:gocritic for InitCliConfig
haitham911 Mar 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ linters-settings:
- name: add-constant
arguments:
- maxLitCount: "3"
allowStrs: '""'
allowStrs: '"","error","path","import"'
allowInts: "0,1,2,3,4"
allowFloats: "0.0,0.,1.0,1.,2.0,2."
- name: argument-limit
Expand All @@ -114,7 +114,7 @@ linters-settings:
- name: comment-spacings
severity: warning
disabled: false
exclude: [""]
exclude: []
arguments:
- mypragma
- otherpragma
Expand Down
44 changes: 44 additions & 0 deletions pkg/config/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
### Import Config

```mermaid
flowchart TD
A["Load Configuration File"] --> B{"Import Section Exists?"}

B -- Yes --> C["Process Imports in Order"]
C --> D{"Import Type?"}
D --> E["Remote URL"]
D --> F["Specific Path"]
D --> G["Wildcard Globs"]

E --> H["Fetch Config from Remote URL"]
F --> I["Read Config from Filesystem"]
G --> I["Read Config from Filesystem"]

H --> J["Call Load Configuration File (Recursively)"]
I --> J["Call Load Configuration File (Recursively)"]

J --> L["Deep Merge with Current Config in Memory"]
L --> K{"More Imports to Process?"}
K -- Yes --> C
K -- No --> M["Configuration Processing Complete"]

%% Loopback for recursion
J -.-> A

%% Styling for clarity
style A fill:#A8DADC,stroke:#1D3557,stroke-width:2px,color:#000000
style B fill:#F4A261,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style C fill:#457B9D,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style D fill:#A8DADC,stroke:#1D3557,stroke-width:2px,color:#000000
style E fill:#E63946,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style F fill:#E63946,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style G fill:#E63946,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style H fill:#A8DADC,stroke:#1D3557,stroke-width:2px,color:#000000
style I fill:#A8DADC,stroke:#1D3557,stroke-width:2px,color:#000000
style J fill:#F4A261,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style L fill:#457B9D,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style K fill:#F4A261,stroke:#1D3557,stroke-width:2px,color:#FFFFFF
style M fill:#1D3557,stroke:#1D3557,stroke-width:2px,color:#FFFFFF

classDef recursion stroke-dasharray: 5 5;
```
291 changes: 1 addition & 290 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
@@ -1,279 +1,24 @@
package config

import (
"bytes"
"encoding/json"
"fmt"
"os"
"path/filepath"
"runtime"

"github.com/mitchellh/go-homedir"
"github.com/pkg/errors"
"github.com/spf13/viper"

"github.com/cloudposse/atmos/internal/tui/templates"
"github.com/cloudposse/atmos/pkg/schema"
"github.com/cloudposse/atmos/pkg/ui/theme"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/cloudposse/atmos/pkg/version"
)

var (
NotFound = errors.New("\n'atmos.yaml' CLI config was not found in any of the searched paths: system dir, home dir, current dir, ENV vars." +
"\nYou can download a sample config and adapt it to your requirements from " +
"https://raw.githubusercontent.com/cloudposse/atmos/main/examples/quick-start-advanced/atmos.yaml")

defaultCliConfig = schema.AtmosConfiguration{
Default: true,
BasePath: ".",
Stacks: schema.Stacks{
BasePath: "stacks",
NamePattern: "{tenant}-{environment}-{stage}",
IncludedPaths: []string{
"orgs/**/*",
},
ExcludedPaths: []string{
"**/_defaults.yaml",
},
},
Components: schema.Components{
Terraform: schema.Terraform{
BasePath: "components/terraform",
ApplyAutoApprove: false,
DeployRunInit: true,
InitRunReconfigure: true,
AutoGenerateBackendFile: true,
AppendUserAgent: fmt.Sprintf("Atmos/%s (Cloud Posse; +https://atmos.tools)", version.Version),
},
Helmfile: schema.Helmfile{
BasePath: "components/helmfile",
KubeconfigPath: "",
HelmAwsProfilePattern: "{namespace}-{tenant}-gbl-{stage}-helm",
ClusterNamePattern: "{namespace}-{tenant}-{environment}-{stage}-eks-cluster",
UseEKS: true,
},
},
Settings: schema.AtmosSettings{
ListMergeStrategy: "replace",
Terminal: schema.Terminal{
MaxWidth: templates.GetTerminalWidth(),
Pager: true,
Colors: true,
Unicode: true,
SyntaxHighlighting: schema.SyntaxHighlighting{
Enabled: true,
Formatter: "terminal",
Theme: "dracula",
HighlightedOutputPager: true,
LineNumbers: true,
Wrap: false,
},
},
},
Workflows: schema.Workflows{
BasePath: "stacks/workflows",
},
Logs: schema.Logs{
File: "/dev/stderr",
Level: "Info",
},
Schemas: schema.Schemas{
JsonSchema: schema.JsonSchema{
BasePath: "stacks/schemas/jsonschema",
},
Opa: schema.Opa{
BasePath: "stacks/schemas/opa",
},
},
Templates: schema.Templates{
Settings: schema.TemplatesSettings{
Enabled: true,
Sprig: schema.TemplatesSettingsSprig{
Enabled: true,
},
Gomplate: schema.TemplatesSettingsGomplate{
Enabled: true,
Datasources: make(map[string]schema.TemplatesSettingsGomplateDatasource),
},
},
},
Initialized: true,
Version: schema.Version{
Check: schema.VersionCheck{
Enabled: true,
Timeout: 1000,
Frequency: "daily",
},
},
}
)

// InitCliConfig finds and merges CLI configurations in the following order: system dir, home dir, current dir, ENV vars, command-line arguments
// https://dev.to/techschoolguru/load-config-from-file-environment-variables-in-golang-with-viper-2j2d
// https://medium.com/@bnprashanth256/reading-configuration-files-and-environment-variables-in-go-golang-c2607f912b63
func InitCliConfig(configAndStacksInfo schema.ConfigAndStacksInfo, processStacks bool) (schema.AtmosConfiguration, error) {

Check failure on line 17 in pkg/config/config.go

View workflow job for this annotation

GitHub Actions / golangci

[golangci] pkg/config/config.go#L17

cognitive complexity 23 of func `InitCliConfig` is high (> 20) (gocognit)
Raw output
pkg/config/config.go:17:1: cognitive complexity 23 of func `InitCliConfig` is high (> 20) (gocognit)
func InitCliConfig(configAndStacksInfo schema.ConfigAndStacksInfo, processStacks bool) (schema.AtmosConfiguration, error) {
^
// atmosConfig is loaded from the following locations (from lower to higher priority):
// system dir (`/usr/local/etc/atmos` on Linux, `%LOCALAPPDATA%/atmos` on Windows)
// home dir (~/.atmos)
// current directory
// ENV vars
// Command-line arguments

var atmosConfig schema.AtmosConfiguration
var err error
configFound := false
var found bool

v := viper.New()
v.SetConfigType("yaml")
v.SetTypeByDefaultValue(true)

// Default configuration values
v.SetDefault("components.helmfile.use_eks", true)
v.SetDefault("components.terraform.append_user_agent", fmt.Sprintf("Atmos/%s (Cloud Posse; +https://atmos.tools)", version.Version))
v.SetDefault("settings.inject_github_token", true)

v.SetDefault("logs.file", "/dev/stderr")
v.SetDefault("logs.level", "Info")

// Process config in system folder
configFilePath1 := ""
atmosConfigFilePath := ""
// https://pureinfotech.com/list-environment-variables-windows-10/
// https://docs.microsoft.com/en-us/windows/deployment/usmt/usmt-recognized-environment-variables
// https://softwareengineering.stackexchange.com/questions/299869/where-is-the-appropriate-place-to-put-application-configuration-files-for-each-p
// https://stackoverflow.com/questions/37946282/why-does-appdata-in-windows-7-seemingly-points-to-wrong-folder
if runtime.GOOS == "windows" {
appDataDir := os.Getenv(WindowsAppDataEnvVar)
if len(appDataDir) > 0 {
configFilePath1 = appDataDir
}
} else {
configFilePath1 = SystemDirConfigFilePath
}

if len(configFilePath1) > 0 {
configFile1 := filepath.Join(configFilePath1, CliConfigFileName)
found, err = processConfigFile(atmosConfig, configFile1, v)
if err != nil {
return atmosConfig, err
}
if found {
configFound = true
atmosConfigFilePath = configFile1
}
}

// Process config in user's HOME dir
configFilePath2, err := homedir.Dir()
if err != nil {
return atmosConfig, err
}
configFile2 := filepath.Join(configFilePath2, ".atmos", CliConfigFileName)
found, err = processConfigFile(atmosConfig, configFile2, v)
if err != nil {
return atmosConfig, err
}
if found {
configFound = true
atmosConfigFilePath = configFile2
}

// Process config in the current dir
configFilePath3, err := os.Getwd()
if err != nil {
return atmosConfig, err
}
configFile3 := filepath.Join(configFilePath3, CliConfigFileName)
found, err = processConfigFile(atmosConfig, configFile3, v)
if err != nil {
return atmosConfig, err
}
if found {
configFound = true
atmosConfigFilePath = configFile3
}

// Process config from the path in ENV var `ATMOS_CLI_CONFIG_PATH`
configFilePath4 := os.Getenv("ATMOS_CLI_CONFIG_PATH")
if len(configFilePath4) > 0 {
u.LogTrace(fmt.Sprintf("Found ENV var ATMOS_CLI_CONFIG_PATH=%s", configFilePath4))
configFile4 := filepath.Join(configFilePath4, CliConfigFileName)
found, err = processConfigFile(atmosConfig, configFile4, v)
if err != nil {
return atmosConfig, err
}
if found {
configFound = true
atmosConfigFilePath = configFile4
}
}

// Process config from the path specified in the Terraform provider (which calls into the atmos code)
if configAndStacksInfo.AtmosCliConfigPath != "" {
configFilePath5 := configAndStacksInfo.AtmosCliConfigPath
if len(configFilePath5) > 0 {
configFile5 := filepath.Join(configFilePath5, CliConfigFileName)
found, err = processConfigFile(atmosConfig, configFile5, v)
if err != nil {
return atmosConfig, err
}
if found {
configFound = true
atmosConfigFilePath = configFile5
}
}
}

if !configFound {
// If `atmos.yaml` not found, use the default config
// Set `ATMOS_LOGS_LEVEL` ENV var to "Debug" to see the message about Atmos using the default CLI config
logsLevelEnvVar := os.Getenv("ATMOS_LOGS_LEVEL")
if logsLevelEnvVar == u.LogLevelDebug || logsLevelEnvVar == u.LogLevelTrace {
u.PrintMessageInColor("'atmos.yaml' CLI config was not found in any of the searched paths: system dir, home dir, current dir, ENV vars.\n"+
"Refer to https://atmos.tools/cli/configuration for details on how to configure 'atmos.yaml'.\n"+
"Using the default CLI config:\n\n", theme.Colors.Info)

err = u.PrintAsYAMLToFileDescriptor(atmosConfig, defaultCliConfig)
if err != nil {
return atmosConfig, err
}
fmt.Println()
}

j, err := json.Marshal(defaultCliConfig)
if err != nil {
return atmosConfig, err
}

reader := bytes.NewReader(j)
err = v.MergeConfig(reader)
if err != nil {
return atmosConfig, err
}
}
// We want the editorconfig color by default to be true
atmosConfig.Validate.EditorConfig.Color = true
// https://gist.github.com/chazcheadle/45bf85b793dea2b71bd05ebaa3c28644
// https://sagikazarmark.hu/blog/decoding-custom-formats-with-viper/
err = v.Unmarshal(&atmosConfig)
atmosConfig, err := LoadConfig(&configAndStacksInfo)
if err != nil {
return atmosConfig, err
}

// Set the CLI config path in the atmosConfig struct
// get dir of atmosConfigFilePath
atmosConfigDir := filepath.Dir(atmosConfigFilePath)
if filepath.IsAbs(atmosConfigDir) {
atmosConfig.CliConfigPath = atmosConfigDir
} else {
absPath, err := filepath.Abs(atmosConfigDir)
if err != nil {
return atmosConfig, err
}
atmosConfig.CliConfigPath = absPath
}
// Process ENV vars
err = processEnvVars(&atmosConfig)
if err != nil {
Expand Down Expand Up @@ -388,37 +133,3 @@
atmosConfig.Initialized = true
return atmosConfig, nil
}

// https://github.com/NCAR/go-figure
// https://github.com/spf13/viper/issues/181
// https://medium.com/@bnprashanth256/reading-configuration-files-and-environment-variables-in-go-golang-c2607f912b63
func processConfigFile(
atmosConfig schema.AtmosConfiguration,
path string,
v *viper.Viper,
) (bool, error) {
// Check if the config file exists
configPath, fileExists := u.SearchConfigFile(path)
if !fileExists {
return false, nil
}

reader, err := os.Open(configPath)
if err != nil {
return false, err
}

defer func(reader *os.File) {
err := reader.Close()
if err != nil {
u.LogWarning(fmt.Sprintf("error closing file '%s'. %v", configPath, err))
}
}(reader)

err = v.MergeConfig(reader)
if err != nil {
return false, err
}

return true, nil
}
Loading
Loading