terragrunt icon indicating copy to clipboard operation
terragrunt copied to clipboard

dependencyBlocksToModuleDependencies does not resolve relative config_path values

Open gnadaban opened this issue 4 years ago • 1 comments

The dependencyBlocksToModuleDependencies function should resolve config_path values relative to the terragrunt.hcl file they are declared in instead of simply appending them to an array.

This becomes a problem during multi-level dependencies:

Terragrunt A has a dependency on Terragrunt B: A -> B Terragrunt C has a dependency on Terragrunt A: C -> A

Eg. in my case:

sqs/data-queue/terragrunt.hcl depends on sqs/deadletter-queue/terragrunt.hcl lambda-function/terragrunt.hcl depends on sqs/data-queue/terragrunt.hcl

Deploying the sqs/data-queue/terragrunt.hcl from its own directory properly finds the dependency. This means, that in this case the path get_terragrunt_dir() gets resolved to sqs/data-queue as it should.

However, when I try to deploy lambda-function/terragrunt.hcl which depends on sqs/data-queue/terragrunt.hcl, I get an error:

[terragrunt] 2021/02/10 16:24:10 Terraform version: 0.13.5
[terragrunt] 2021/02/10 16:24:10 Reading Terragrunt config file at <redacted>/lambda-function/terragrunt.hcl
[terragrunt] 2021/02/10 16:24:10 Error reading file at path <redacted>/iam/sqs/deadletter-queue: open <redacted>/iam/sqs/deadletter-queue: no such file or directory

The interesting thing about the path in the log, is that it comes from sqs/data-queue/terragrunt.hcl where it is defined so:

dependency "deadletter_queue" {
  config_path = abspath("${get_terragrunt_dir()}/../deadletter-queue")
}

I have also tried using .. instead of the get_terragrunt_dir() function, with and without the abspath function. These also don't work obviously, as there's no path resolution done in dependencyBlocksToModuleDependencies:

func dependencyBlocksToModuleDependencies(decodedDependencyBlocks []Dependency) *ModuleDependencies {
	if len(decodedDependencyBlocks) == 0 {
		return nil
	}

	paths := []string{}
	for _, decodedDependencyBlock := range decodedDependencyBlocks {
		configPath := decodedDependencyBlock.ConfigPath
		if util.IsFile(configPath) && filepath.Base(configPath) == DefaultTerragruntConfigPath {
			// dependencies system expects the directory containing the terragrunt.hcl file
			configPath = filepath.Dir(configPath)
		}
		paths = append(paths, configPath)
	}
	return &ModuleDependencies{Paths: paths}
}

If this function would simply resolve the configPath values to canonical paths based on where the dependency is declared, it would not only work correctly, but the expected path would be self-explanatory from a developer point of view.

gnadaban avatar Feb 10 '21 22:02 gnadaban

Oddly the examples in the docs here are using this non-working notation: https://terragrunt.gruntwork.io/docs/features/keep-your-terragrunt-architecture-dry/

The dependency blocks:

dependency "vpc" {
  config_path = "../vpc"
}

dependency "mysql" {
  config_path = "../mysql"
}

that are moved to _env/app.hcl for added DRY'ness does not work unless changed to

dependency "vpc" {
  config_path = "${get_terragrunt_dir()}/../vpc"
}

dependency "mysql" {
  config_path = "${get_terragrunt_dir()}/../mysql"
}

svinther avatar Aug 28 '22 11:08 svinther