github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/configs/module_call.go (about) 1 package configs 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/hcl/v2" 7 "github.com/hashicorp/hcl/v2/gohcl" 8 "github.com/hashicorp/hcl/v2/hclsyntax" 9 ) 10 11 // ModuleCall represents a "module" block in a module or file. 12 type ModuleCall struct { 13 Name string 14 15 SourceAddr string 16 SourceAddrRange hcl.Range 17 SourceSet bool 18 19 Config hcl.Body 20 21 Version VersionConstraint 22 23 Count hcl.Expression 24 ForEach hcl.Expression 25 26 Providers []PassedProviderConfig 27 28 DependsOn []hcl.Traversal 29 30 DeclRange hcl.Range 31 } 32 33 func decodeModuleBlock(block *hcl.Block, override bool) (*ModuleCall, hcl.Diagnostics) { 34 mc := &ModuleCall{ 35 Name: block.Labels[0], 36 DeclRange: block.DefRange, 37 } 38 39 schema := moduleBlockSchema 40 if override { 41 schema = schemaForOverrides(schema) 42 } 43 44 content, remain, diags := block.Body.PartialContent(schema) 45 mc.Config = remain 46 47 if !hclsyntax.ValidIdentifier(mc.Name) { 48 diags = append(diags, &hcl.Diagnostic{ 49 Severity: hcl.DiagError, 50 Summary: "Invalid module instance name", 51 Detail: badIdentifierDetail, 52 Subject: &block.LabelRanges[0], 53 }) 54 } 55 56 if attr, exists := content.Attributes["source"]; exists { 57 valDiags := gohcl.DecodeExpression(attr.Expr, nil, &mc.SourceAddr) 58 diags = append(diags, valDiags...) 59 mc.SourceAddrRange = attr.Expr.Range() 60 mc.SourceSet = true 61 } 62 63 if attr, exists := content.Attributes["version"]; exists { 64 var versionDiags hcl.Diagnostics 65 mc.Version, versionDiags = decodeVersionConstraint(attr) 66 diags = append(diags, versionDiags...) 67 } 68 69 if attr, exists := content.Attributes["count"]; exists { 70 mc.Count = attr.Expr 71 72 // We currently parse this, but don't yet do anything with it. 73 diags = append(diags, &hcl.Diagnostic{ 74 Severity: hcl.DiagError, 75 Summary: "Reserved argument name in module block", 76 Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), 77 Subject: &attr.NameRange, 78 }) 79 } 80 81 if attr, exists := content.Attributes["for_each"]; exists { 82 mc.ForEach = attr.Expr 83 84 // We currently parse this, but don't yet do anything with it. 85 diags = append(diags, &hcl.Diagnostic{ 86 Severity: hcl.DiagError, 87 Summary: "Reserved argument name in module block", 88 Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), 89 Subject: &attr.NameRange, 90 }) 91 } 92 93 if attr, exists := content.Attributes["depends_on"]; exists { 94 deps, depsDiags := decodeDependsOn(attr) 95 diags = append(diags, depsDiags...) 96 mc.DependsOn = append(mc.DependsOn, deps...) 97 98 // We currently parse this, but don't yet do anything with it. 99 diags = append(diags, &hcl.Diagnostic{ 100 Severity: hcl.DiagError, 101 Summary: "Reserved argument name in module block", 102 Detail: fmt.Sprintf("The name %q is reserved for use in a future version of Terraform.", attr.Name), 103 Subject: &attr.NameRange, 104 }) 105 } 106 107 if attr, exists := content.Attributes["providers"]; exists { 108 seen := make(map[string]hcl.Range) 109 pairs, pDiags := hcl.ExprMap(attr.Expr) 110 diags = append(diags, pDiags...) 111 for _, pair := range pairs { 112 key, keyDiags := decodeProviderConfigRef(pair.Key, "providers") 113 diags = append(diags, keyDiags...) 114 value, valueDiags := decodeProviderConfigRef(pair.Value, "providers") 115 diags = append(diags, valueDiags...) 116 if keyDiags.HasErrors() || valueDiags.HasErrors() { 117 continue 118 } 119 120 matchKey := key.String() 121 if prev, exists := seen[matchKey]; exists { 122 diags = append(diags, &hcl.Diagnostic{ 123 Severity: hcl.DiagError, 124 Summary: "Duplicate provider address", 125 Detail: fmt.Sprintf("A provider configuration was already passed to %s at %s. Each child provider configuration can be assigned only once.", matchKey, prev), 126 Subject: pair.Value.Range().Ptr(), 127 }) 128 continue 129 } 130 131 rng := hcl.RangeBetween(pair.Key.Range(), pair.Value.Range()) 132 seen[matchKey] = rng 133 mc.Providers = append(mc.Providers, PassedProviderConfig{ 134 InChild: key, 135 InParent: value, 136 }) 137 } 138 } 139 140 // Reserved block types (all of them) 141 for _, block := range content.Blocks { 142 diags = append(diags, &hcl.Diagnostic{ 143 Severity: hcl.DiagError, 144 Summary: "Reserved block type name in module block", 145 Detail: fmt.Sprintf("The block type name %q is reserved for use by Terraform in a future version.", block.Type), 146 Subject: &block.TypeRange, 147 }) 148 } 149 150 return mc, diags 151 } 152 153 // PassedProviderConfig represents a provider config explicitly passed down to 154 // a child module, possibly giving it a new local address in the process. 155 type PassedProviderConfig struct { 156 InChild *ProviderConfigRef 157 InParent *ProviderConfigRef 158 } 159 160 var moduleBlockSchema = &hcl.BodySchema{ 161 Attributes: []hcl.AttributeSchema{ 162 { 163 Name: "source", 164 Required: true, 165 }, 166 { 167 Name: "version", 168 }, 169 { 170 Name: "count", 171 }, 172 { 173 Name: "for_each", 174 }, 175 { 176 Name: "depends_on", 177 }, 178 { 179 Name: "providers", 180 }, 181 }, 182 Blocks: []hcl.BlockHeaderSchema{ 183 // These are all reserved for future use. 184 {Type: "lifecycle"}, 185 {Type: "locals"}, 186 {Type: "provider", LabelNames: []string{"type"}}, 187 }, 188 }