|
1 | 1 | package helper
|
2 | 2 |
|
3 | 3 | import (
|
| 4 | + "github.com/hashicorp/go-version" |
4 | 5 | "github.com/hashicorp/hcl/v2"
|
5 | 6 | "github.com/hashicorp/hcl/v2/gohcl"
|
6 | 7 | "github.com/terraform-linters/tflint-plugin-sdk/terraform"
|
@@ -137,6 +138,32 @@ func (r *Runner) WalkResources(resourceType string, walker func(*terraform.Resou
|
137 | 138 |
|
138 | 139 | // WalkModuleCalls visits all module calls from Files.
|
139 | 140 | func (r *Runner) WalkModuleCalls(walker func(*terraform.ModuleCall) error) error {
|
| 141 | + for _, file := range r.Files { |
| 142 | + calls, _, diags := file.Body.PartialContent(&hcl.BodySchema{ |
| 143 | + Blocks: []hcl.BlockHeaderSchema{ |
| 144 | + { |
| 145 | + Type: "module", |
| 146 | + LabelNames: []string{"name"}, |
| 147 | + }, |
| 148 | + }, |
| 149 | + }) |
| 150 | + if diags.HasErrors() { |
| 151 | + return diags |
| 152 | + } |
| 153 | + |
| 154 | + for _, block := range calls.Blocks { |
| 155 | + call, diags := simpleDecodeModuleCallBlock(block) |
| 156 | + if diags.HasErrors() { |
| 157 | + return diags |
| 158 | + } |
| 159 | + |
| 160 | + err := walker(call) |
| 161 | + if err != nil { |
| 162 | + return err |
| 163 | + } |
| 164 | + } |
| 165 | + } |
| 166 | + |
140 | 167 | return nil
|
141 | 168 | }
|
142 | 169 |
|
@@ -256,21 +283,10 @@ func simpleDecodeResouceBlock(resource *hcl.Block) (*terraform.Resource, hcl.Dia
|
256 | 283 |
|
257 | 284 | var ref *terraform.ProviderConfigRef
|
258 | 285 | if attr, exists := content.Attributes["provider"]; exists {
|
259 |
| - traversal, diags := hcl.AbsTraversalForExpr(attr.Expr) |
| 286 | + ref, diags = decodeProviderConfigRef(attr.Expr) |
260 | 287 | if diags.HasErrors() {
|
261 | 288 | return nil, diags
|
262 | 289 | }
|
263 |
| - |
264 |
| - ref = &terraform.ProviderConfigRef{ |
265 |
| - Name: traversal.RootName(), |
266 |
| - NameRange: traversal[0].SourceRange(), |
267 |
| - } |
268 |
| - |
269 |
| - if len(traversal) > 1 { |
270 |
| - aliasStep := traversal[1].(hcl.TraverseAttr) |
271 |
| - ref.Alias = aliasStep.Name |
272 |
| - ref.AliasRange = aliasStep.SourceRange().Ptr() |
273 |
| - } |
274 | 290 | }
|
275 | 291 |
|
276 | 292 | managed := &terraform.ManagedResource{}
|
@@ -384,3 +400,109 @@ func simpleDecodeResouceBlock(resource *hcl.Block) (*terraform.Resource, hcl.Dia
|
384 | 400 | TypeRange: resource.LabelRanges[0],
|
385 | 401 | }, nil
|
386 | 402 | }
|
| 403 | + |
| 404 | +func simpleDecodeModuleCallBlock(block *hcl.Block) (*terraform.ModuleCall, hcl.Diagnostics) { |
| 405 | + content, remain, diags := block.Body.PartialContent(&hcl.BodySchema{ |
| 406 | + Attributes: []hcl.AttributeSchema{ |
| 407 | + {Name: "source", Required: true}, |
| 408 | + {Name: "version"}, |
| 409 | + {Name: "providers"}, |
| 410 | + }, |
| 411 | + }) |
| 412 | + if diags.HasErrors() { |
| 413 | + return nil, diags |
| 414 | + } |
| 415 | + |
| 416 | + var sourceAddr string |
| 417 | + var sourceAddrRange hcl.Range |
| 418 | + if attr, exists := content.Attributes["source"]; exists { |
| 419 | + if diags := gohcl.DecodeExpression(attr.Expr, nil, &sourceAddr); diags.HasErrors() { |
| 420 | + return nil, diags |
| 421 | + } |
| 422 | + sourceAddrRange = attr.Expr.Range() |
| 423 | + } |
| 424 | + |
| 425 | + providers := []terraform.PassedProviderConfig{} |
| 426 | + if attr, exists := content.Attributes["providers"]; exists { |
| 427 | + pairs, diags := hcl.ExprMap(attr.Expr) |
| 428 | + if diags.HasErrors() { |
| 429 | + return nil, diags |
| 430 | + } |
| 431 | + |
| 432 | + for _, pair := range pairs { |
| 433 | + key, diags := decodeProviderConfigRef(pair.Key) |
| 434 | + if diags.HasErrors() { |
| 435 | + return nil, diags |
| 436 | + } |
| 437 | + |
| 438 | + value, diags := decodeProviderConfigRef(pair.Value) |
| 439 | + if diags.HasErrors() { |
| 440 | + return nil, diags |
| 441 | + } |
| 442 | + |
| 443 | + providers = append(providers, terraform.PassedProviderConfig{ |
| 444 | + InChild: key, |
| 445 | + InParent: value, |
| 446 | + }) |
| 447 | + } |
| 448 | + } |
| 449 | + |
| 450 | + var versionRequired version.Constraints |
| 451 | + var versionValue string |
| 452 | + var versionRange hcl.Range |
| 453 | + var err error |
| 454 | + if attr, exists := content.Attributes["version"]; exists { |
| 455 | + versionRange = attr.Expr.Range() |
| 456 | + |
| 457 | + if diags := gohcl.DecodeExpression(attr.Expr, nil, &versionValue); diags.HasErrors() { |
| 458 | + return nil, diags |
| 459 | + } |
| 460 | + |
| 461 | + versionRequired, err = version.NewConstraint(versionValue) |
| 462 | + if err != nil { |
| 463 | + return nil, hcl.Diagnostics{ |
| 464 | + {Severity: hcl.DiagError, Summary: "Invalid version constraint"}, |
| 465 | + } |
| 466 | + } |
| 467 | + } |
| 468 | + |
| 469 | + return &terraform.ModuleCall{ |
| 470 | + Name: block.Labels[0], |
| 471 | + |
| 472 | + SourceAddr: sourceAddr, |
| 473 | + SourceAddrRange: sourceAddrRange, |
| 474 | + SourceSet: !sourceAddrRange.Empty(), |
| 475 | + |
| 476 | + Config: remain, |
| 477 | + ConfigRange: block.DefRange, |
| 478 | + |
| 479 | + Version: terraform.VersionConstraint{ |
| 480 | + Required: versionRequired, |
| 481 | + DeclRange: versionRange, |
| 482 | + }, |
| 483 | + |
| 484 | + Providers: providers, |
| 485 | + |
| 486 | + DeclRange: block.DefRange, |
| 487 | + }, nil |
| 488 | +} |
| 489 | + |
| 490 | +func decodeProviderConfigRef(expr hcl.Expression) (*terraform.ProviderConfigRef, hcl.Diagnostics) { |
| 491 | + traversal, diags := hcl.AbsTraversalForExpr(expr) |
| 492 | + if diags.HasErrors() { |
| 493 | + return nil, diags |
| 494 | + } |
| 495 | + |
| 496 | + ref := &terraform.ProviderConfigRef{ |
| 497 | + Name: traversal.RootName(), |
| 498 | + NameRange: traversal[0].SourceRange(), |
| 499 | + } |
| 500 | + |
| 501 | + if len(traversal) > 1 { |
| 502 | + aliasStep := traversal[1].(hcl.TraverseAttr) |
| 503 | + ref.Alias = aliasStep.Name |
| 504 | + ref.AliasRange = aliasStep.SourceRange().Ptr() |
| 505 | + } |
| 506 | + |
| 507 | + return ref, nil |
| 508 | +} |
0 commit comments