-
Notifications
You must be signed in to change notification settings - Fork 2.4k
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(terraform): eval submodules #6411
Changes from 1 commit
e64d935
2e439e3
6ad7e54
58efb65
51453e8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1522,3 +1522,106 @@ func compareSets(a []int, b []int) bool { | |
|
||
return true | ||
} | ||
|
||
func TestModuleRefersToOutputOfAnotherModule(t *testing.T) { | ||
files := map[string]string{ | ||
"main.tf": ` | ||
module "module2" { | ||
source = "./modules/foo" | ||
} | ||
|
||
module "module1" { | ||
source = "./modules/bar" | ||
test_var = module.module2.test_out | ||
} | ||
`, | ||
"modules/foo/main.tf": ` | ||
output "test_out" { | ||
value = "test_value" | ||
} | ||
`, | ||
"modules/bar/main.tf": ` | ||
variable "test_var" {} | ||
|
||
resource "test_resource" "this" { | ||
dynamic "dynamic_block" { | ||
for_each = [var.test_var] | ||
content { | ||
some_attr = dynamic_block.value | ||
} | ||
} | ||
} | ||
`, | ||
} | ||
|
||
modules := parse(t, files) | ||
require.Len(t, modules, 3) | ||
|
||
resources := modules.GetResourcesByType("test_resource") | ||
require.Len(t, resources, 1) | ||
|
||
attr, _ := resources[0].GetNestedAttribute("dynamic_block.some_attr") | ||
require.NotNil(t, attr) | ||
|
||
assert.Equal(t, "test_value", attr.GetRawValue()) | ||
} | ||
|
||
func TestCyclicModules(t *testing.T) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is a nice test case! I'm curious what logic prevents the cyclic loop? In this case module1 uses module2 and vice-versa but how do we figure out to resolve them both? does the logic still hold true if we have 3 modules in a cycle? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Cycling between modules is allowed. Modules are evaluated until the output variables of all modules stop changing or the maximum number of evaluations is reached. In short, if at least one output variable of any module has been updated, we have one unevaluated module and therefore continue the evaluation. Let's look at an example:
The first evaluation step will completely compute |
||
files := map[string]string{ | ||
"main.tf": ` | ||
module "module2" { | ||
source = "./modules/foo" | ||
test_var = module.module1.test_out | ||
} | ||
|
||
module "module1" { | ||
source = "./modules/bar" | ||
test_var = module.module2.test_out | ||
} | ||
`, | ||
"modules/foo/main.tf": ` | ||
variable "test_var" {} | ||
|
||
resource "test_resource" "this" { | ||
dynamic "dynamic_block" { | ||
for_each = [var.test_var] | ||
content { | ||
some_attr = dynamic_block.value | ||
} | ||
} | ||
} | ||
|
||
output "test_out" { | ||
value = "test_value" | ||
} | ||
`, | ||
"modules/bar/main.tf": ` | ||
variable "test_var" {} | ||
|
||
resource "test_resource" "this" { | ||
dynamic "dynamic_block" { | ||
for_each = [var.test_var] | ||
content { | ||
some_attr = dynamic_block.value | ||
} | ||
} | ||
} | ||
|
||
output "test_out" { | ||
value = test_resource.this.dynamic_block.some_attr | ||
} | ||
`, | ||
} | ||
|
||
modules := parse(t, files) | ||
require.Len(t, modules, 3) | ||
|
||
resources := modules.GetResourcesByType("test_resource") | ||
require.Len(t, resources, 2) | ||
|
||
for _, res := range resources { | ||
attr, _ := res.GetNestedAttribute("dynamic_block.some_attr") | ||
require.NotNil(t, attr, res.FullName()) | ||
assert.Equal(t, "test_value", attr.GetRawValue()) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm curious why this didn't work as intended, is it because we weren't
definition.Parser.Load(ctx)
for each definition? Like so: https://github.com/aquasecurity/trivy/pull/6411/files#diff-b10704f6636c4e99c08df82aeb21c2283a75a61953d50b6f800289dbfa44979eR187There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modules are loaded and evaluated in alphabetical order. But modules can have different order and cyclic relationships, so we need to evaluate each module several times. Load is just a new function to load modules once, which returns an evaluator.