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

feat: Support Relative Path Imports #891

Merged
merged 48 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
c50d071
Handle relative paths for stack imports
milldr Dec 24, 2024
f79e16a
Tests for relative paths
milldr Dec 24, 2024
74ab1d8
Refactor handling of relative paths in ProcessImportSection
milldr Dec 24, 2024
e2af2db
Handle relative paths in StackImport.Path and string imports
milldr Dec 24, 2024
9d39012
Add new test2/us-west-1.yaml file and update test counts
milldr Dec 24, 2024
12358b0
Update import paths in us-west-1.yaml file
milldr Dec 24, 2024
093ac5e
Merge branch 'main' into feat/DEV-1856_relative-paths
milldr Dec 24, 2024
b9856ff
Merge branch 'main' into feat/DEV-1856_relative-paths
osterman Dec 26, 2024
71eaca0
Merge branch 'main' into feat/DEV-1856_relative-paths
milldr Dec 26, 2024
4f6170c
Merge branch 'feat/DEV-1856_relative-paths' of github.com:cloudposse/…
milldr Dec 26, 2024
362e979
Merge branch 'main' into feat/DEV-1856_relative-paths
milldr Dec 27, 2024
8d76059
Refactor handling of import paths in ProcessImportSection
milldr Dec 27, 2024
1beeb8a
Resolve relative paths in ProcessImportSection function
milldr Dec 27, 2024
dd55c0f
Update internal/exec/stack_processor_utils.go
milldr Dec 27, 2024
dfa5a84
Refactor resolveRelativePath for improved path handling
milldr Dec 27, 2024
507a5dd
Refactor filepath handling for better readability
milldr Dec 27, 2024
cafdfdd
Remove unnecessary path normalization code and simplify logic
milldr Dec 27, 2024
ddde3d5
Improve path resolution handling in stack_processor_utils
milldr Dec 27, 2024
4eeef33
Merge branch 'main' into feat/DEV-1856_relative-paths
milldr Jan 2, 2025
7fa809e
relative path handling improvements for windows; added security check…
milldr Jan 3, 2025
3be0d81
Validate import paths for security and base path inclusion
milldr Jan 3, 2025
2f89d4a
Update path.Join calls to use filepath.Join for consistency
milldr Jan 3, 2025
2c80a89
Update stack_processor_utils.go to use filepath package
milldr Jan 3, 2025
062cf05
validate stack paths to check against the global base path
milldr Jan 3, 2025
3d98cd4
Refactor path resolution and validation logic
milldr Jan 3, 2025
558c324
Refactor resolving relative path logic for clarity
milldr Jan 3, 2025
88740a5
Revert changes
milldr Jan 3, 2025
94886b6
Refactor path resolution for consistency
milldr Jan 3, 2025
1bc256a
Refactor resolveRelativePath function for path consistency
milldr Jan 3, 2025
037e0ba
Convert relative path to base path if starts with "." or ".."
milldr Jan 3, 2025
358128c
Simplify resolving relative paths in stack_processor_utils
milldr Jan 3, 2025
7bf9208
added tests for atmos stacks with relative paths
milldr Jan 3, 2025
29b390d
Merge branch 'main' into feat/DEV-1856_relative-paths
milldr Jan 3, 2025
daefbe1
merge
milldr Jan 7, 2025
10d41dc
PR feedback
milldr Jan 7, 2025
9767879
merged main, incorporate new test fixture pattern
milldr Jan 7, 2025
fcf08fc
Merge branch 'main' into feat/DEV-1856_relative-paths
milldr Jan 9, 2025
229aa91
Add test case for atmos stacks with relative paths
milldr Jan 9, 2025
8e47d61
Delete nonprod and prod cache weather files
milldr Jan 9, 2025
b215eca
Delete outdated Atmos manifest JSON schema
milldr Jan 9, 2025
bcd13ac
consistent indentation
milldr Jan 9, 2025
57b557f
Add ignore rule for cache text files
milldr Jan 9, 2025
6faf6dd
Update stack processor test cases and relative paths
milldr Jan 9, 2025
187e61b
revert cahnges
milldr Jan 9, 2025
a482907
use relative paths in advanced usage example
milldr Jan 9, 2025
dc897c7
use relative paths in advanced usage example
milldr Jan 9, 2025
a427817
reset advanced usage
milldr Jan 9, 2025
fb6ac32
added relative paths to demo-context
milldr Jan 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@
.direnv/

.atmos/cache.yaml
**/cache.*.txt
2 changes: 1 addition & 1 deletion examples/demo-context/stacks/deploy/dev/demo.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

import:
- deploy/_defaults
- ../_defaults
- catalog/demo
- mixins/west-coast

Expand Down
2 changes: 1 addition & 1 deletion examples/demo-context/stacks/deploy/prod/demo.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

import:
- deploy/_defaults
- ../_defaults
- catalog/demo
- mixins/east-coast

Expand Down
2 changes: 1 addition & 1 deletion examples/demo-context/stacks/deploy/staging/demo.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

import:
- deploy/_defaults
- ../_defaults
- catalog/demo
- mixins/east-coast

Expand Down
44 changes: 38 additions & 6 deletions internal/exec/stack_processor_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ func ProcessYAMLConfigFiles(
map[string]map[string]any,
error,
) {

count := len(filePaths)
listResult := make([]string, count)
mapResult := map[string]any{}
Expand Down Expand Up @@ -170,7 +169,6 @@ func ProcessYAMLConfigFile(
map[string]any,
error,
) {

var stackConfigs []map[string]any
relativeFilePath := u.TrimBasePathFromPath(basePath+"/", filePath)

Expand Down Expand Up @@ -468,6 +466,7 @@ func ProcessYAMLConfigFile(
if err != nil {
return nil, nil, nil, nil, nil, err
}

importRelativePathWithExt := strings.Replace(filepath.ToSlash(importFile), filepath.ToSlash(basePath)+"/", "", 1)
ext2 := filepath.Ext(importRelativePathWithExt)
if ext2 == "" {
Expand Down Expand Up @@ -1779,11 +1778,42 @@ func FindComponentDependenciesLegacy(
return unique, nil
}

// resolveRelativePath checks if a path is relative to the current directory and if so,
// resolves it relative to the current file's directory. It ensures the resolved path
// exists within the base path.
func resolveRelativePath(path string, currentFilePath string) string {
if path == "" {
return path
}

// Convert all paths to use forward slashes for consistency in processing
normalizedPath := filepath.ToSlash(path)
normalizedCurrentFilePath := filepath.ToSlash(currentFilePath)

// Atmos import paths are generally relative paths, however, there are two types of relative paths:
// 1. Paths relative to the base path (most common) - e.g. "mixins/region/us-east-2"
// 2. Paths relative to the current file's directory (less common) - e.g. "./_defaults" imports will be relative to `./`
//
// Here we check if the path starts with "." or ".." to identify if it's relative to the current file.
// If it is, we'll convert it to be relative to the file doing the import, rather than the `base_path`.
parts := strings.Split(normalizedPath, "/")
firstElement := filepath.Clean(parts[0])
if firstElement == "." || firstElement == ".." {
milldr marked this conversation as resolved.
Show resolved Hide resolved
// Join the current local path with the current stack file path
baseDir := filepath.Dir(normalizedCurrentFilePath)
relativePath := filepath.Join(baseDir, normalizedPath)
// Return in original format, OS-specific
return filepath.FromSlash(relativePath)
}
// For non-relative paths, return as-is in original format
return path
}
milldr marked this conversation as resolved.
Show resolved Hide resolved

// ProcessImportSection processes the `import` section in stack manifests
// The `import` section` can be of the following types:
// 1. list of `StackImport` structs
// 2. list of strings
// 3. List of strings and `StackImport` structs in the same file
// The `import` section can contain:
// 1. Project-relative paths (e.g. "mixins/region/us-east-2")
// 2. Paths relative to the current stack file (e.g. "./_defaults")
// 3. StackImport structs containing either of the above path types (e.g. "path: mixins/region/us-east-2")
func ProcessImportSection(stackMap map[string]any, filePath string) ([]schema.StackImport, error) {
stackImports, ok := stackMap[cfg.ImportSectionName]

Expand All @@ -1809,6 +1839,7 @@ func ProcessImportSection(stackMap map[string]any, filePath string) ([]schema.St
var importObj schema.StackImport
err := mapstructure.Decode(imp, &importObj)
if err == nil {
importObj.Path = resolveRelativePath(importObj.Path, filePath)
milldr marked this conversation as resolved.
Show resolved Hide resolved
result = append(result, importObj)
continue
}
Expand All @@ -1822,6 +1853,7 @@ func ProcessImportSection(stackMap map[string]any, filePath string) ([]schema.St
return nil, fmt.Errorf("invalid empty import in the file '%s'", filePath)
}

s = resolveRelativePath(s, filePath)
result = append(result, schema.StackImport{Path: s})
}

Expand Down
52 changes: 52 additions & 0 deletions pkg/stack/stack_processor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,55 @@ func TestStackProcessor(t *testing.T) {
assert.Nil(t, err)
t.Log(string(yamlConfig))
}

func TestStackProcessorRelativePaths(t *testing.T) {
stacksBasePath := "../../tests/fixtures/scenarios/relative-paths/stacks"
terraformComponentsBasePath := "../../tests/fixtures/components/terraform"

filePaths := []string{
"../../tests/fixtures/scenarios/relative-paths/stacks/orgs/acme/platform/dev.yaml",
"../../tests/fixtures/scenarios/relative-paths/stacks/orgs/acme/platform/prod.yaml",
}

atmosConfig := schema.AtmosConfiguration{
Templates: schema.Templates{
Settings: schema.TemplatesSettings{
Enabled: true,
Sprig: schema.TemplatesSettingsSprig{
Enabled: true,
},
Gomplate: schema.TemplatesSettingsGomplate{
Enabled: true,
},
},
},
}

listResult, mapResult, _, err := ProcessYAMLConfigFiles(
atmosConfig,
stacksBasePath,
terraformComponentsBasePath,
"",
filePaths,
true,
true,
false,
)

assert.Nil(t, err)
assert.Equal(t, 2, len(listResult))
assert.Equal(t, 2, len(mapResult))

mapResultKeys := u.StringKeysFromMap(mapResult)
assert.Equal(t, "orgs/acme/platform/dev", mapResultKeys[0])
assert.Equal(t, "orgs/acme/platform/prod", mapResultKeys[1])

mapConfig1, err := u.UnmarshalYAML[schema.AtmosSectionMapType](listResult[0])
assert.Nil(t, err)

components := mapConfig1["components"].(map[string]any)
terraformComponents := components["terraform"].(map[string]any)

myappComponent := terraformComponents["myapp"].(map[string]any)
assert.NotNil(t, myappComponent)
}
21 changes: 21 additions & 0 deletions tests/fixtures/scenarios/relative-paths/atmos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
base_path: "./"

components:
terraform:
base_path: "components/terraform"
apply_auto_approve: false
deploy_run_init: true
init_run_reconfigure: true
auto_generate_backend_file: false

stacks:
base_path: "stacks"
included_paths:
- "orgs/**/*"
excluded_paths:
- "**/_defaults.yaml"
name_pattern: "{namespace}-{tenant}-{stage}"

logs:
file: "/dev/stderr"
level: Info
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Example Terraform Weather Component

This Terraform "root" module fetches weather information for a specified location with custom display options.
It queries data from the [`wttr.in`](https://wttr.in) weather service and stores the result in a local file (`cache.txt`).
It also provides several outputs like weather information, request URL, stage, location, language, and units of measurement.

## Features

- Fetch weather updates for a location using HTTP request.
- Write the obtained weather data in a local file.
- Customizable display options.
- View the request URL.
- Get informed about the stage, location, language, and units in the metadata.

## Usage

To include this module in your [Atmos Stacks](https://atmos.tools/core-concepts/stacks) configuration:

```yaml
components:
terraform:
weather:
vars:
stage: dev
location: New York
options: 0T
format: v2
lang: en
units: m
```

### Inputs
- `stage`: Stage where it will be deployed.
- `location`: Location for which the weather is reported. Default is "Los Angeles".
- `options`: Options to customize the output. Default is "0T".
- `format`: Specifies the output format. Default is "v2".
- `lang`: Language in which the weather will be displayed. Default is "en".
- `units`: Units in which the weather will be displayed. Default is "m".

### Outputs
- `weather`: The fetched weather data.
- `url`: Requested URL.
- `stage`: Stage of deployment.
- `location`: Location of the reported weather.
- `lang`: Language used for weather data.
- `units`: Units of measurement for the weather data.

Please note, this module requires Terraform version >=1.0.0, and you need to specify no other required providers.

Happy Weather Tracking!
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
locals {
url = format("https://wttr.in/%v?%v&format=%v&lang=%v&u=%v",
urlencode(var.location),
urlencode(var.options),
urlencode(var.format),
urlencode(var.lang),
urlencode(var.units),
)
}

data "http" "weather" {
url = local.url
request_headers = {
User-Agent = "curl"
}
}

# Now write this to a file (as an example of a resource)
resource "local_file" "cache" {
filename = "cache.${var.stage}.txt"
content = data.http.weather.response_body
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
output "weather" {
value = data.http.weather.response_body
}

output "url" {
value = local.url
}

output "stage" {
value = var.stage
description = "Stage where it was deployed"
}

output "location" {
value = var.location
description = "Location of the weather report."
}

output "lang" {
value = var.lang
description = "Language which the weather is displayed."
}

output "units" {
value = var.units
description = "Units the weather is displayed."
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
variable "stage" {
description = "Stage where it will be deployed"
type = string
}

variable "location" {
description = "Location for which the weather."
type = string
default = "Los Angeles"
}

variable "options" {
description = "Options to customize the output."
type = string
default = "0T"
}

variable "format" {
description = "Format of the output."
type = string
default = "v2"
}

variable "lang" {
description = "Language in which the weather is displayed."
type = string
default = "en"
}

variable "units" {
description = "Units in which the weather is displayed."
type = string
default = "m"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
terraform {
required_version = ">= 1.0.0"

required_providers {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

components:
terraform:
myapp:
vars:
location: Los Angeles
lang: en
format: ''
options: '0'
units: m
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

vars:
namespace: acme
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

import:
- ../_defaults

vars:
tenant: platform
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

vars:
stage: dev

import:
- ./_defaults
- catalog/myapp

components:
terraform:
myapp:
vars:
location: Stockholm
lang: se
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# yaml-language-server: $schema=https://atmos.tools/schemas/atmos/atmos-manifest/1.0/atmos-manifest.json

vars:
stage: prod

import:
- ./_defaults
- catalog/myapp

components:
terraform:
myapp:
vars:
location: Los Angeles
lang: en
Loading
Loading