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

fixes infinte loop while local variable resolution #700

Merged
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
9 changes: 6 additions & 3 deletions pkg/iac-providers/terraform/commons/local-references.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func getLocalName(localRef string) (string, string) {
}

// ResolveLocalRef returns the local value as configured in IaC config in module
func (r *RefResolver) ResolveLocalRef(localRef string) interface{} {
func (r *RefResolver) ResolveLocalRef(localRef, callerRef string) interface{} {

// get local name from localRef
localName, localExpr := getLocalName(localRef)
Expand Down Expand Up @@ -89,8 +89,11 @@ func (r *RefResolver) ResolveLocalRef(localRef string) interface{} {
if reflect.TypeOf(val).Kind() == reflect.String {
valStr := val.(string)
resolvedVal := strings.Replace(localRef, localExpr, valStr, 1)
zap.S().Debugf("resolved str local value ref: '%v', value: '%v'", localRef, resolvedVal)
return r.ResolveStrRef(resolvedVal)
if callerRef != "" && strings.Contains(resolvedVal, callerRef) {
zap.S().Debugf("resolved str local value ref: '%v', value: '%v'", localRef, resolvedVal)
return resolvedVal
}
return r.ResolveStrRef(resolvedVal, localRef)
}

// return extracted value
Expand Down
6 changes: 3 additions & 3 deletions pkg/iac-providers/terraform/commons/lookup-references.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,13 @@ func getLookupName(lookupRef string) (string, string, string) {
}

// ResolveLookupRef returns the lookup value as configured in IaC config in module
func (r *RefResolver) ResolveLookupRef(lookupRef string) interface{} {
func (r *RefResolver) ResolveLookupRef(lookupRef, callerRef string) interface{} {

// get lookup name from lookupRef
table, key, _ := getLookupName(lookupRef)

// resolve key, if it is a reference
resolvedKey := r.ResolveStrRef(key)
resolvedKey := r.ResolveStrRef(key, callerRef)

// check if key is still an unresolved reference
if reflect.TypeOf(resolvedKey).Kind() == reflect.String && isRef(resolvedKey.(string)) {
Expand All @@ -71,7 +71,7 @@ func (r *RefResolver) ResolveLookupRef(lookupRef string) interface{} {
}

// resolve table, if it is a ref
lookup := r.ResolveStrRef(table)
lookup := r.ResolveStrRef(table, callerRef)

// check if lookup is a map
if reflect.TypeOf(lookup).String() != "map[string]interface {}" {
Expand Down
9 changes: 6 additions & 3 deletions pkg/iac-providers/terraform/commons/module-references.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func getModuleVarName(moduleRef string) (string, string, string) {
}

// ResolveModuleRef tries to resolve cross module references
func (r *RefResolver) ResolveModuleRef(moduleRef string,
func (r *RefResolver) ResolveModuleRef(moduleRef, callerRef string,
children map[string]*hclConfigs.Config) interface{} {

// get module and variable name
Expand Down Expand Up @@ -108,8 +108,11 @@ func (r *RefResolver) ResolveModuleRef(moduleRef string,
zap.S().Debugf("resolved str variable ref refers to self: '%v'", moduleRef)
return moduleRef
}
zap.S().Debugf("resolved str variable ref: '%v', value: '%v'", moduleRef, resolvedVal)
return r.ResolveStrRef(resolvedVal)
if callerRef != "" && strings.Contains(resolvedVal, callerRef) {
zap.S().Debugf("resolved str variable ref: '%v', value: '%v'", moduleRef, resolvedVal)
return resolvedVal
}
return r.ResolveStrRef(resolvedVal, moduleRef)
}
return val
}
20 changes: 10 additions & 10 deletions pkg/iac-providers/terraform/commons/references.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ func (r *RefResolver) ResolveRefs(config jsonObj) jsonObj {

// case 1: config value is a string; in resource config, refs
// are of the type string
config[k] = r.ResolveStrRef(v.(string))
config[k] = r.ResolveStrRef(v.(string), "")

case vType == "tfv12.jsonObj" && vKind == reflect.Map:

Expand All @@ -86,7 +86,7 @@ func (r *RefResolver) ResolveRefs(config jsonObj) jsonObj {
// references
if len(sConfig) > 0 && reflect.TypeOf(sConfig[0]).Kind() == reflect.String {
for i, c := range sConfig {
sConfig[i] = r.ResolveStrRef(c.(string))
sConfig[i] = r.ResolveStrRef(c.(string), "")
}
config[k] = sConfig
}
Expand Down Expand Up @@ -115,23 +115,23 @@ func (r *RefResolver) ResolveRefs(config jsonObj) jsonObj {
// ResolveStrRef tries to resolve a string reference. Reference can be a
// variable "${var.foo}", cross module variable "${module.foo.bar}", local
// value "${local.foo}"
func (r *RefResolver) ResolveStrRef(ref string) interface{} {
func (r *RefResolver) ResolveStrRef(ref, callerRef string) interface{} {

switch {
case isModuleRef(ref):

// resolve cross module references
return r.ResolveModuleRef(ref, r.Config.Children)
return r.ResolveModuleRef(ref, callerRef, r.Config.Children)

case isLocalRef(ref):

// resolve local value references
return r.ResolveLocalRef(ref)
return r.ResolveLocalRef(ref, callerRef)

case isLookupRef(ref):

// resolve lookup references
return r.ResolveLookupRef(ref)
return r.ResolveLookupRef(ref, callerRef)

case isVarRef(ref):

Expand All @@ -144,7 +144,7 @@ func (r *RefResolver) ResolveStrRef(ref string) interface{} {
*/

// 1. resolve variables initialized in parent module call
val := r.ResolveVarRefFromParentModuleCall(ref)
val := r.ResolveVarRefFromParentModuleCall(ref, callerRef)

if reflect.TypeOf(val).Kind() == reflect.String {

Expand All @@ -163,17 +163,17 @@ func (r *RefResolver) ResolveStrRef(ref string) interface{} {
case isVarRef(valStr):

// resolve variable reference
return r.ResolveVarRef(valStr)
return r.ResolveVarRef(valStr, ref)

case isModuleRef(valStr):

// resolve cross module reference in parent module
return r.ResolveModuleRef(valStr, r.Config.Parent.Children)
return r.ResolveModuleRef(valStr, ref, r.Config.Parent.Children)

case isRef(valStr):

// some other reference
return r.ResolveStrRef(valStr)
return r.ResolveStrRef(valStr, ref)
}
}

Expand Down
18 changes: 12 additions & 6 deletions pkg/iac-providers/terraform/commons/variable-references.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func getVarName(varRef string) (string, string) {
}

// ResolveVarRef returns the variable value as configured in IaC config in module
func (r *RefResolver) ResolveVarRef(varRef string) interface{} {
func (r *RefResolver) ResolveVarRef(varRef, callerRef string) interface{} {

// get variable name from varRef
varName, varExpr := getVarName(varRef)
Expand Down Expand Up @@ -94,8 +94,11 @@ func (r *RefResolver) ResolveVarRef(varRef string) interface{} {
zap.S().Debugf("resolved str variable ref refers to self: '%v'", varRef)
return varRef
}
zap.S().Debugf("resolved str variable ref: '%v', value: '%v'", varRef, resolvedVal)
return r.ResolveStrRef(resolvedVal)
if callerRef != "" && strings.Contains(resolvedVal, callerRef) {
zap.S().Debugf("resolved str variable ref: '%v', value: '%v'", varRef, string(resolvedVal))
return resolvedVal
}
return r.ResolveStrRef(resolvedVal, varRef)
}
return val
}
Expand All @@ -104,7 +107,7 @@ func (r *RefResolver) ResolveVarRef(varRef string) interface{} {
// ModuleCall from parent module. The resolved value can be an absolute value
// (string, int, bool etc.) or it can also be another reference, which may
// need further resolution
func (r *RefResolver) ResolveVarRefFromParentModuleCall(varRef string) interface{} {
func (r *RefResolver) ResolveVarRefFromParentModuleCall(varRef, callerRef string) interface{} {

zap.S().Debugf("resolving variable ref %q in parent module call", varRef)

Expand Down Expand Up @@ -152,8 +155,11 @@ func (r *RefResolver) ResolveVarRefFromParentModuleCall(varRef string) interface
zap.S().Debugf("resolved str variable ref refers to self: '%v'", varRef)
return resolvedVal
}
zap.S().Debugf("resolved str variable ref: '%v', value: '%v'", varRef, string(resolvedVal))
return r.ResolveStrRef(resolvedVal)
if callerRef != "" && strings.Contains(resolvedVal, callerRef) {
zap.S().Debugf("resolved str variable ref: '%v', value: '%v'", varRef, string(resolvedVal))
return resolvedVal
}
return r.ResolveStrRef(resolvedVal, varRef)
}

// return extracted value
Expand Down
11 changes: 9 additions & 2 deletions pkg/iac-providers/terraform/v14/load-dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,8 +121,15 @@ func TestLoadIacDir(t *testing.T) {
},
{
name: "recursive loop while resolving variables",
tfConfigDir: filepath.Join(testDataDir, "recursive-loop"),
tfJSONFile: filepath.Join(tfJSONDir, "recursive-loop.json"),
tfConfigDir: filepath.Join(testDataDir, "recursive-loop-variables"),
tfJSONFile: filepath.Join(tfJSONDir, "recursive-loop-variables.json"),
tfv14: TfV14{},
wantErr: nil,
},
{
name: "recursive loop while resolving locals",
tfConfigDir: filepath.Join(testDataDir, "recursive-loop-locals"),
tfJSONFile: filepath.Join(tfJSONDir, "recursive-loop-locals.json"),
tfv14: TfV14{},
wantErr: nil,
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
locals {
foo = "bar"
}

module "dummy" {
source = "./dummy"
bar = local.foo
foo = "bar"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
variable bar {
type = string
}

variable foo {
type = string
}

locals {
foo = lower(var.bar != null ? var.bar : var.foo)
}

resource "aws_iam_user" "lb" {
name = local.foo
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"aws_iam_user": [
{
"id": "aws_iam_user.lb",
"name": "lb",
"source": "dummy/main.tf",
"line": 13,
"type": "aws_iam_user",
"config": {
"name": "${lower(${local.foo} != null ? var.bar : var.foo)}"
},
"skip_rules": null
}
]
}