-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Description
Bug: run --all fails to match dependencies in queue when symlinks are used
Describe the bug
When using terragrunt run --all apply with a symlinked directory, dependencies are not correctly matched to queue entries because paths are stored in incompatible formats:
- Queue entries use relative paths:
./namespace - Dependencies use broken hybrid paths after symlink resolution:
../../../../../real/path/to/stacks/.../namespace
This causes all dependencies to be marked as "not in queue, considering it ready", resulting in all units running in parallel regardless of their actual dependencies.
Environment
- Terragrunt version: v0.97.2
- Terraform version: 1.14.3
- OS: Linux (container)
Steps to reproduce
- Create a directory structure:
/app/
├── terraform/
│ └── stacks/
│ └── myproject/
│ ├── namespace/
│ │ └── terragrunt.hcl
│ ├── service-a/
│ │ └── terragrunt.hcl # depends on namespace
│ └── service-b/
│ └── terragrunt.hcl # depends on namespace
- Create a symlink to the terraform directory:
ln -s /app/terraform /terraform- In the terragrunt.hcl files, use the symlink path for dependencies:
# service-a/terragrunt.hcl
dependency "namespace" {
config_path = "/terraform/stacks/myproject/namespace"
}
inputs = {
namespace = dependency.namespace.outputs.name
}- Run terragrunt from the project directory:
cd /app/terraform/stacks/myproject
terragrunt run --all applyExpected behavior
Dependencies should be correctly matched to queue entries regardless of symlink resolution. Units should run in dependency order:
namespaceruns firstservice-aandservice-bwait fornamespaceto complete
Actual behavior
All units start simultaneously because dependencies are not matched in the queue.
Debug output shows the path mismatch:
Unit queue will be processed for apply in this order:
- Unit namespace
- Unit service-a
- Unit service-b
Runner Pool Controller: starting with 3 tasks, concurrency 2147483647
Dependency /app/terraform/stacks/myproject/namespace is not in queue, considering it ready
And when listing units with dependencies:
=> Unit service-a (excluded: false, assume applied: false, dependencies: [../../../../../app/terraform/stacks/myproject/namespace])
=> Unit service-b (excluded: false, assume applied: false, dependencies: [../../../../../app/terraform/stacks/myproject/namespace])
=> Unit namespace (excluded: false, assume applied: false, dependencies: [])
Notice:
- Queue entries use:
./namespace - Dependencies use:
../../../../../app/terraform/stacks/myproject/namespace
The path comparison in entryByPathUnsafe fails:
// internal/queue/queue.go line ~193
func (q *Queue) entryByPathUnsafe(path string) *Entry {
for _, entry := range q.Entries {
if entry.Component.Path() == path { // This comparison fails!
return entry
}
}
return nil
}Root cause analysis
The issue appears to be in path normalization when symlinks are involved:
config_path = "/terraform/stacks/.../namespace"is an absolute path through a symlink- During discovery,
filepath.EvalSymlinks()resolves this to/app/terraform/stacks/.../namespace - This is then converted to a relative path using
filepath.Rel()from the working directory - The resulting path
../../../../../app/terraform/stacks/.../namespaceis a broken hybrid (goes up then uses absolute path components) - Queue entries are stored with simple relative paths like
./namespace - The string comparison
==fails because the formats don't match
Workaround
Use bind mounts instead of symlinks, or use the real absolute path in config_path instead of the symlinked path.
Additional context
This worked correctly in Terragrunt v0.93.x. The regression appears to be related to path handling changes in the v0.95+ releases.
Related issues that may share root cause:
- Symlinks in Terragrunt #1611 - Symlinks in Terragrunt
- dependency block with a relative path does not resolve anymore when included with expose=true #3475 - dependency block with relative path does not resolve with expose=true
- path_relative_to_include and symlinks #543 - path_relative_to_include and symlinks