Skip to content

Commit

Permalink
Fix issue with parsing for a cascading dependencies case, Fix Duplica…
Browse files Browse the repository at this point in the history
…ted dependencies, Handle Relative/Absolute Paths more gracefully (#113)

* Add test case for cascading dependencies bug

* Dependencies: Fix parse for cascade, Fix duplicates

- Improvement: In order to fix the issue with cascading dependencies this add extra checks
to handle both the case that a dependency uses an absolute path or a relative one.
- Improvement: Also converts relative dependencies to absolute to avoid duplicates and
re-converts the dependencies to relative before the configuration file is
generated.
- Fix: Creates new terragrunt options with the dependency path in order to be
able to parse successfully the cascading dependencies.
  • Loading branch information
angeloskaltsikis authored Feb 4, 2021
1 parent b8d7c61 commit 25f1d46
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 8 deletions.
22 changes: 14 additions & 8 deletions cmd/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,12 @@ func getDependencies(path string, terragruntOptions *options.TerragruntOptions)
nonEmptyDeps := []string{}
for _, dep := range dependencies {
if dep != "" {
nonEmptyDeps = append(nonEmptyDeps, dep)
childDepAbsPath := dep
if !filepath.IsAbs(childDepAbsPath) {
childDepAbsPath = makePathAbsolute(dep, path)
}
childDepAbsPath = filepath.ToSlash(childDepAbsPath)
nonEmptyDeps = append(nonEmptyDeps, childDepAbsPath)
}
}

Expand All @@ -157,12 +162,9 @@ func getDependencies(path string, terragruntOptions *options.TerragruntOptions)
continue
}

// To find the path to the dependency, we join three things:
// 1. The path to the current module, `path`
// 2. `..`, because `path` includes the `terragrunt.hcl` file extension, while the `dep` path is relative to the folder that file is in
// 3. the relative path from the current module to the dependency, `dep`
depPath := filepath.Join(path, "..", dep)
childDeps, err := getDependencies(depPath, terragruntOptions)
depPath := dep
terrOpts, err := options.NewTerragruntOptions(depPath)
childDeps, err := getDependencies(depPath, terrOpts)
if err != nil {
continue
}
Expand Down Expand Up @@ -237,11 +239,15 @@ func createProject(sourcePath string) (*AtlantisProject, error) {

// Add other dependencies based on their relative paths. We always want to output with Unix path separators
for _, dependencyPath := range dependencies {
absolutePath := makePathAbsolute(dependencyPath, sourcePath)
absolutePath := dependencyPath
if !filepath.IsAbs(absolutePath) {
absolutePath = makePathAbsolute(dependencyPath, sourcePath)
}
relativePath, err := filepath.Rel(absoluteSourceDir, absolutePath)
if err != nil {
return nil, err
}

relativeDependencies = append(relativeDependencies, filepath.ToSlash(relativePath))
}

Expand Down
8 changes: 8 additions & 0 deletions cmd/generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,14 @@ func TestInfrastructureLive(t *testing.T) {
})
}

func TestInfrastructureMutliAccountsVPCRoute53TGWCascading(t *testing.T) {
runTest(t, filepath.Join("golden", "multi_accounts_vpc_route53_tgw.yaml"), []string{
"--root",
filepath.Join("..", "test_examples", "multi_accounts_vpc_route53_tgw"),
"--cascade-dependencies",
})
}

func TestAutoPlan(t *testing.T) {
runTest(t, filepath.Join("golden", "autoplan.yaml"), []string{
"--root",
Expand Down
26 changes: 26 additions & 0 deletions cmd/golden/multi_accounts_vpc_route53_tgw.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
automerge: false
parallel_apply: true
parallel_plan: true
projects:
- autoplan:
enabled: false
when_modified:
- '*.hcl'
- '*.tf*'
dir: network-account/eu-west-1/network/transit-gateway
- autoplan:
enabled: false
when_modified:
- '*.hcl'
- '*.tf*'
- ../../../env-a/network/vpc/terragrunt.hcl
- ../../../../../network-account/eu-west-1/network/transit-gateway/terragrunt.hcl
dir: prod/eu-west-1/_global/route53/test-zone
- autoplan:
enabled: false
when_modified:
- '*.hcl'
- '*.tf*'
- ../../../../../network-account/eu-west-1/network/transit-gateway/terragrunt.hcl
dir: prod/eu-west-1/env-a/network/vpc
version: 3
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
locals {
env = "network"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the
# working directory, into a temporary folder, and execute your Terraform commands in that folder.
terraform {
source = "git::[email protected]:gruntwork-io/terragrunt-infrastructure-modules-example.git//tgw?ref=v0.3.0"
}

# Include all settings from the root terragrunt.hcl file
include {
path = find_in_parent_folders()
}

locals {
# Automatically load environment-level variables
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
# Extract out common variables for reuse
env_name = local.env_vars.locals.env
}

inputs = {
name = "tgw-${local.env_name}"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
locals {
env = "global-region"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
locals {
# Automatically load environment-level variables
environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))

# Extract out common variables for reuse
env = local.environment_vars.locals.env
}

# Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the
# working directory, into a temporary folder, and execute your Terraform commands in that folder.
terraform {
source = "git::[email protected]:gruntwork-io/terragrunt-infrastructure-modules-example.git//zone?ref=v0.3.0"
}

# Include all settings from the root terragrunt.hcl file
include {
path = find_in_parent_folders()
}

dependency "vpc" {
config_path = "../../../env-a/network/vpc/"
}

# These are the variables we have to pass in to use the module specified in the terragrunt configuration above
inputs = {
name = "test_zone_${local.env}"
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
locals {
env = "env-a"
stack_name = "Environment-a"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Terragrunt will copy the Terraform configurations specified by the source parameter, along with any files in the
# working directory, into a temporary folder, and execute your Terraform commands in that folder.
terraform {
source = "git::[email protected]:gruntwork-io/terragrunt-infrastructure-modules-example.git//vpc?ref=v0.3.0"
}

# Include all settings from the root terragrunt.hcl file
include {
path = find_in_parent_folders()
}

locals {
# Automatically load environment-level variables
env_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))
# Extract out common variables for reuse
stack_name = local.env_vars.locals.stack_name
}

dependency "tgw" {
config_path = "../../../../../network-account/eu-west-1/network/transit-gateway/"
}

inputs = {
name = local.stack_name
transit_gateway_id = dependency.tgw.outputs.this_ec2_transit_gateway_id
}
18 changes: 18 additions & 0 deletions test_examples/multi_accounts_vpc_route53_tgw/terragrunt.hcl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
locals {

# Automatically load environment-level variables
environment_vars = read_terragrunt_config(find_in_parent_folders("env.hcl"))

}

# ---------------------------------------------------------------------------------------------------------------------
# GLOBAL PARAMETERS
# These variables apply to all configurations in this subfolder. These are automatically merged into the child
# `terragrunt.hcl` config via the include block.
# ---------------------------------------------------------------------------------------------------------------------

# Configure root level variables that all resources can inherit. This is especially helpful with multi-account configs
# where terraform_remote_state data sources are placed directly into the modules.
inputs = merge(
local.environment_vars.locals,
)

0 comments on commit 25f1d46

Please sign in to comment.