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

fix: warn if Terragrunt dependency is outside project. #1983

Merged
merged 1 commit into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ Given a version number `MAJOR.MINOR.PATCH`, we increment the:

- Fix the command-line parsing of `run` and `script run` which were not failing from unknown flags.
- Fix `create --all-terragrunt` creating Terragrunt stacks with cycles.
- Panic in the Terragrunt integration when the project had modules with dependency paths outside the current Terramate project.
i4ki marked this conversation as resolved.
Show resolved Hide resolved
- Now Terramate throw a warning for such configurations.
i4ki marked this conversation as resolved.
Show resolved Hide resolved

## v0.11.3

Expand Down
2 changes: 1 addition & 1 deletion cmd/terramate/cli/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -1609,7 +1609,7 @@ func (c *cli) initTerragrunt() {
for _, otherMod := range mod.After.Strings() {
// Parent stack modules must be excluded because of implicit filesystem ordering.
// Parent stacks are always executed before child stacks.
if mod.Path.HasPrefix(otherMod + "/") {
if otherMod == "/" || mod.Path.HasPrefix(otherMod+"/") {
continue
}
// after stacks must not be defined as child stacks
Expand Down
62 changes: 62 additions & 0 deletions e2etests/core/create_all_terragrunt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,68 @@ func TestCreateAllTerragrunt(t *testing.T) {
StderrRegex: "Found a dependency cycle between modules",
},
},
{
name: "Terragrunt with dependency defined outside of the Terramate project",
layout: []string{
hclfile("terragrunt.hcl", Doc(
Block("terraform",
Str("source", "github.com/some/repo"),
),
Block("dependency",
Labels("module2"),
Str("config_path", `../other`),
),
Block("dependencies",
Expr("paths", `["../other"]`),
))),
hclfile("../other/terragrunt.hcl", Doc(
Block("terraform",
Str("source", "github.com/some/repo"),
),
)),
},
want: RunExpected{
StderrRegexes: []string{
regexp.QuoteMeta("Warning: Dependency outside of Terramate project detected in `dependency.config_path` configuration. Ignoring."),
regexp.QuoteMeta("Warning: Dependency outside of Terramate project detected in `dependencies.paths` configuration. Ignoring."),
},
Stdout: nljoin(
"Created stack /",
),
},
wantOrder: []string{"."},
},
{
name: "Terragrunt with dependency defined outside of the Terramate project (sharing same base path)",
layout: []string{
hclfile("terragrunt.hcl", Doc(
Block("terraform",
Str("source", "github.com/some/repo"),
),
Block("dependency",
Labels("module2"),
Str("config_path", `../sandboxy`),
),
Block("dependencies",
Expr("paths", `["../sandboxy"]`),
))),
hclfile("../sandboxy/terragrunt.hcl", Doc(
Block("terraform",
Str("source", "github.com/some/repo"),
),
)),
},
want: RunExpected{
StderrRegexes: []string{
regexp.QuoteMeta("Warning: Dependency outside of Terramate project detected in `dependency.config_path` configuration. Ignoring."),
regexp.QuoteMeta("Warning: Dependency outside of Terramate project detected in `dependencies.paths` configuration. Ignoring."),
},
Stdout: nljoin(
"Created stack /",
),
},
wantOrder: []string{"."},
},
} {
tc := tc
t.Run(tc.name, func(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion project/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,14 @@ func PrjAbsPath(root, abspath string) Path {
if !filepath.IsAbs(abspath) {
panic(fmt.Errorf("path %q is not absolute", abspath))
}
if root == abspath {
return NewPath("/")
}
if !strings.HasSuffix(root, string(filepath.Separator)) { // accounts for root=/
root = root + string(filepath.Separator)
}
if !strings.HasPrefix(abspath, root) {
panic(fmt.Errorf("abspath %q does not start with root %q", abspath, root))
panic(fmt.Errorf(`abspath %q does not start with root %q`, abspath, root))
}
d := filepath.ToSlash(strings.TrimPrefix(abspath, root))
if d == "" {
Expand Down
32 changes: 25 additions & 7 deletions tg/tg_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ package tg

import (
"context"
"fmt"
"io"
"os"
"path"
"path/filepath"
"sort"
"strings"

"github.com/gruntwork-io/go-commons/env"
"github.com/gruntwork-io/terragrunt/config"
Expand All @@ -18,6 +19,7 @@ import (
"github.com/gruntwork-io/terragrunt/util"
"github.com/rs/zerolog/log"
"github.com/terramate-io/terramate/errors"
"github.com/terramate-io/terramate/printer"
"github.com/terramate-io/terramate/project"
"github.com/zclconf/go-cty/cty/function"
)
Expand Down Expand Up @@ -180,6 +182,11 @@ func ScanModules(rootdir string, dir project.Path, trackDependencies bool) (Modu
if !filepath.IsAbs(depPath) {
depPath = filepath.Join(tgMod.Path, depPath)
}
if depPath != rootdir && !strings.HasPrefix(depPath, rootdir+string(filepath.Separator)) {
warnDependencyOutsideProject(mod, depPath, "dependency.config_path")

continue
}
dependsOn[project.PrjAbsPath(rootdir, depPath)] = struct{}{}
}
}
Expand All @@ -191,14 +198,17 @@ func ScanModules(rootdir string, dir project.Path, trackDependencies bool) (Modu
}

if tgConfig.Dependencies != nil {
for _, dep := range tgConfig.Dependencies.Paths {
var p project.Path
if !path.IsAbs(dep) {
p = mod.Path.Join(dep)
} else {
logger.Debug().Str("dep-path", dep).Msg("ignore absolute path")
for _, depPath := range tgConfig.Dependencies.Paths {
if !filepath.IsAbs(depPath) {
depPath = filepath.Join(tgMod.Path, depPath)
}
if depPath != rootdir && !strings.HasPrefix(depPath, rootdir+string(filepath.Separator)) {
warnDependencyOutsideProject(mod, depPath, "dependencies.paths")

continue
}

p := project.PrjAbsPath(rootdir, depPath)
mod.After = append(mod.After, p)
}
mod.After.Sort()
Expand Down Expand Up @@ -252,3 +262,11 @@ func newTerragruntOptions(dir string) *options.TerragruntOptions {

return opts
}

func warnDependencyOutsideProject(mod *Module, dep string, field string) {
printer.Stderr.WarnWithDetails(fmt.Sprintf("Dependency outside of Terramate project detected in `%s` configuration. Ignoring.", field),
errors.E("The Terragrunt module %s depends on the module at %s, which is located outside of the your current"+
" Terramate project. To resolve this ensure the dependent module is part of your Terramate project.",
mod.Path, dep),
)
}
Loading