6 "github.com/hashicorp/terraform/tfdiags"
8 "github.com/hashicorp/hcl2/hcl"
9 "github.com/hashicorp/hcl2/hcl/hclsyntax"
12 // ProviderConfig is the address of a provider configuration.
13 type ProviderConfig struct {
16 // If not empty, Alias identifies which non-default (aliased) provider
17 // configuration this address refers to.
21 // NewDefaultProviderConfig returns the address of the default (un-aliased)
22 // configuration for the provider with the given type name.
23 func NewDefaultProviderConfig(typeName string) ProviderConfig {
24 return ProviderConfig{
29 // ParseProviderConfigCompact parses the given absolute traversal as a relative
30 // provider address in compact form. The following are examples of traversals
31 // that can be successfully parsed as compact relative provider configuration
37 // This function will panic if given a relative traversal.
39 // If the returned diagnostics contains errors then the result value is invalid
40 // and must not be used.
41 func ParseProviderConfigCompact(traversal hcl.Traversal) (ProviderConfig, tfdiags.Diagnostics) {
42 var diags tfdiags.Diagnostics
43 ret := ProviderConfig{
44 Type: traversal.RootName(),
47 if len(traversal) < 2 {
48 // Just a type name, then.
52 aliasStep := traversal[1]
53 switch ts := aliasStep.(type) {
54 case hcl.TraverseAttr:
58 diags = diags.Append(&hcl.Diagnostic{
59 Severity: hcl.DiagError,
60 Summary: "Invalid provider configuration address",
61 Detail: "The provider type name must either stand alone or be followed by an alias name separated with a dot.",
62 Subject: aliasStep.SourceRange().Ptr(),
66 if len(traversal) > 2 {
67 diags = diags.Append(&hcl.Diagnostic{
68 Severity: hcl.DiagError,
69 Summary: "Invalid provider configuration address",
70 Detail: "Extraneous extra operators after provider configuration address.",
71 Subject: traversal[2:].SourceRange().Ptr(),
78 // ParseProviderConfigCompactStr is a helper wrapper around ParseProviderConfigCompact
79 // that takes a string and parses it with the HCL native syntax traversal parser
80 // before interpreting it.
82 // This should be used only in specialized situations since it will cause the
83 // created references to not have any meaningful source location information.
84 // If a reference string is coming from a source that should be identified in
85 // error messages then the caller should instead parse it directly using a
86 // suitable function from the HCL API and pass the traversal itself to
87 // ParseProviderConfigCompact.
89 // Error diagnostics are returned if either the parsing fails or the analysis
90 // of the traversal fails. There is no way for the caller to distinguish the
91 // two kinds of diagnostics programmatically. If error diagnostics are returned
92 // then the returned address is invalid.
93 func ParseProviderConfigCompactStr(str string) (ProviderConfig, tfdiags.Diagnostics) {
94 var diags tfdiags.Diagnostics
96 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
97 diags = diags.Append(parseDiags)
98 if parseDiags.HasErrors() {
99 return ProviderConfig{}, diags
102 addr, addrDiags := ParseProviderConfigCompact(traversal)
103 diags = diags.Append(addrDiags)
107 // Absolute returns an AbsProviderConfig from the receiver and the given module
109 func (pc ProviderConfig) Absolute(module ModuleInstance) AbsProviderConfig {
110 return AbsProviderConfig{
116 func (pc ProviderConfig) String() string {
118 // Should never happen; always indicates a bug
119 return "provider.<invalid>"
123 return fmt.Sprintf("provider.%s.%s", pc.Type, pc.Alias)
126 return "provider." + pc.Type
129 // StringCompact is an alternative to String that returns the form that can
130 // be parsed by ParseProviderConfigCompact, without the "provider." prefix.
131 func (pc ProviderConfig) StringCompact() string {
133 return fmt.Sprintf("%s.%s", pc.Type, pc.Alias)
138 // AbsProviderConfig is the absolute address of a provider configuration
139 // within a particular module instance.
140 type AbsProviderConfig struct {
141 Module ModuleInstance
142 ProviderConfig ProviderConfig
145 // ParseAbsProviderConfig parses the given traversal as an absolute provider
146 // address. The following are examples of traversals that can be successfully
147 // parsed as absolute provider configuration addresses:
151 // module.bar.provider.aws
152 // module.bar.module.baz.provider.aws.foo
153 // module.foo[1].provider.aws.foo
155 // This type of address is used, for example, to record the relationships
156 // between resources and provider configurations in the state structure.
157 // This type of address is not generally used in the UI, except in error
158 // messages that refer to provider configurations.
159 func ParseAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) {
160 modInst, remain, diags := parseModuleInstancePrefix(traversal)
161 ret := AbsProviderConfig{
164 if len(remain) < 2 || remain.RootName() != "provider" {
165 diags = diags.Append(&hcl.Diagnostic{
166 Severity: hcl.DiagError,
167 Summary: "Invalid provider configuration address",
168 Detail: "Provider address must begin with \"provider.\", followed by a provider type name.",
169 Subject: remain.SourceRange().Ptr(),
174 diags = diags.Append(&hcl.Diagnostic{
175 Severity: hcl.DiagError,
176 Summary: "Invalid provider configuration address",
177 Detail: "Extraneous operators after provider configuration alias.",
178 Subject: hcl.Traversal(remain[3:]).SourceRange().Ptr(),
183 if tt, ok := remain[1].(hcl.TraverseAttr); ok {
184 ret.ProviderConfig.Type = tt.Name
186 diags = diags.Append(&hcl.Diagnostic{
187 Severity: hcl.DiagError,
188 Summary: "Invalid provider configuration address",
189 Detail: "The prefix \"provider.\" must be followed by a provider type name.",
190 Subject: remain[1].SourceRange().Ptr(),
195 if len(remain) == 3 {
196 if tt, ok := remain[2].(hcl.TraverseAttr); ok {
197 ret.ProviderConfig.Alias = tt.Name
199 diags = diags.Append(&hcl.Diagnostic{
200 Severity: hcl.DiagError,
201 Summary: "Invalid provider configuration address",
202 Detail: "Provider type name must be followed by a configuration alias name.",
203 Subject: remain[2].SourceRange().Ptr(),
212 // ParseAbsProviderConfigStr is a helper wrapper around ParseAbsProviderConfig
213 // that takes a string and parses it with the HCL native syntax traversal parser
214 // before interpreting it.
216 // This should be used only in specialized situations since it will cause the
217 // created references to not have any meaningful source location information.
218 // If a reference string is coming from a source that should be identified in
219 // error messages then the caller should instead parse it directly using a
220 // suitable function from the HCL API and pass the traversal itself to
221 // ParseAbsProviderConfig.
223 // Error diagnostics are returned if either the parsing fails or the analysis
224 // of the traversal fails. There is no way for the caller to distinguish the
225 // two kinds of diagnostics programmatically. If error diagnostics are returned
226 // the returned address is invalid.
227 func ParseAbsProviderConfigStr(str string) (AbsProviderConfig, tfdiags.Diagnostics) {
228 var diags tfdiags.Diagnostics
230 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1})
231 diags = diags.Append(parseDiags)
232 if parseDiags.HasErrors() {
233 return AbsProviderConfig{}, diags
236 addr, addrDiags := ParseAbsProviderConfig(traversal)
237 diags = diags.Append(addrDiags)
241 // ProviderConfigDefault returns the address of the default provider config
242 // of the given type inside the recieving module instance.
243 func (m ModuleInstance) ProviderConfigDefault(name string) AbsProviderConfig {
244 return AbsProviderConfig{
246 ProviderConfig: ProviderConfig{
252 // ProviderConfigAliased returns the address of an aliased provider config
253 // of with given type and alias inside the recieving module instance.
254 func (m ModuleInstance) ProviderConfigAliased(name, alias string) AbsProviderConfig {
255 return AbsProviderConfig{
257 ProviderConfig: ProviderConfig{
264 // Inherited returns an address that the receiving configuration address might
265 // inherit from in a parent module. The second bool return value indicates if
266 // such inheritance is possible, and thus whether the returned address is valid.
268 // Inheritance is possible only for default (un-aliased) providers in modules
269 // other than the root module. Even if a valid address is returned, inheritence
270 // may not be performed for other reasons, such as if the calling module
271 // provided explicit provider configurations within the call for this module.
272 // The ProviderTransformer graph transform in the main terraform module has
273 // the authoritative logic for provider inheritance, and this method is here
274 // mainly just for its benefit.
275 func (pc AbsProviderConfig) Inherited() (AbsProviderConfig, bool) {
276 // Can't inherit if we're already in the root.
277 if len(pc.Module) == 0 {
278 return AbsProviderConfig{}, false
281 // Can't inherit if we have an alias.
282 if pc.ProviderConfig.Alias != "" {
283 return AbsProviderConfig{}, false
286 // Otherwise, we might inherit from a configuration with the same
287 // provider name in the parent module instance.
288 parentMod := pc.Module.Parent()
289 return pc.ProviderConfig.Absolute(parentMod), true
292 func (pc AbsProviderConfig) String() string {
293 if len(pc.Module) == 0 {
294 return pc.ProviderConfig.String()
296 return fmt.Sprintf("%s.%s", pc.Module.String(), pc.ProviderConfig.String())