github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/addrs/provider_config.go (about) 1 package addrs 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/terraform/tfdiags" 7 8 "github.com/hashicorp/hcl/v2" 9 "github.com/hashicorp/hcl/v2/hclsyntax" 10 ) 11 12 // ProviderConfig is the address of a provider configuration. 13 type ProviderConfig struct { 14 Type Provider 15 16 // If not empty, Alias identifies which non-default (aliased) provider 17 // configuration this address refers to. 18 Alias string 19 } 20 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{ 25 Type: NewLegacyProvider(typeName), 26 } 27 } 28 29 // Absolute returns an AbsProviderConfig from the receiver and the given module 30 // instance address. 31 func (pc ProviderConfig) Absolute(module ModuleInstance) AbsProviderConfig { 32 return AbsProviderConfig{ 33 Module: module, 34 ProviderConfig: pc, 35 } 36 } 37 38 func (pc ProviderConfig) String() string { 39 if pc.Type.LegacyString() == "" { 40 // Should never happen; always indicates a bug 41 return "provider.<invalid>" 42 } 43 44 if pc.Alias != "" { 45 return fmt.Sprintf("provider.%s.%s", pc.Type.LegacyString(), pc.Alias) 46 } 47 48 return "provider." + pc.Type.LegacyString() 49 } 50 51 // StringCompact is an alternative to String that returns the form that can 52 // be parsed by ParseProviderConfigCompact, without the "provider." prefix. 53 func (pc ProviderConfig) StringCompact() string { 54 if pc.Alias != "" { 55 return fmt.Sprintf("%s.%s", pc.Type.LegacyString(), pc.Alias) 56 } 57 return pc.Type.LegacyString() 58 } 59 60 // AbsProviderConfig is the absolute address of a provider configuration 61 // within a particular module instance. 62 type AbsProviderConfig struct { 63 Module ModuleInstance 64 ProviderConfig ProviderConfig 65 } 66 67 // ParseAbsProviderConfig parses the given traversal as an absolute provider 68 // address. The following are examples of traversals that can be successfully 69 // parsed as absolute provider configuration addresses: 70 // 71 // provider.aws 72 // provider.aws.foo 73 // module.bar.provider.aws 74 // module.bar.module.baz.provider.aws.foo 75 // module.foo[1].provider.aws.foo 76 // 77 // This type of address is used, for example, to record the relationships 78 // between resources and provider configurations in the state structure. 79 // This type of address is not generally used in the UI, except in error 80 // messages that refer to provider configurations. 81 func ParseAbsProviderConfig(traversal hcl.Traversal) (AbsProviderConfig, tfdiags.Diagnostics) { 82 modInst, remain, diags := parseModuleInstancePrefix(traversal) 83 ret := AbsProviderConfig{ 84 Module: modInst, 85 } 86 if len(remain) < 2 || remain.RootName() != "provider" { 87 diags = diags.Append(&hcl.Diagnostic{ 88 Severity: hcl.DiagError, 89 Summary: "Invalid provider configuration address", 90 Detail: "Provider address must begin with \"provider.\", followed by a provider type name.", 91 Subject: remain.SourceRange().Ptr(), 92 }) 93 return ret, diags 94 } 95 if len(remain) > 3 { 96 diags = diags.Append(&hcl.Diagnostic{ 97 Severity: hcl.DiagError, 98 Summary: "Invalid provider configuration address", 99 Detail: "Extraneous operators after provider configuration alias.", 100 Subject: hcl.Traversal(remain[3:]).SourceRange().Ptr(), 101 }) 102 return ret, diags 103 } 104 105 if tt, ok := remain[1].(hcl.TraverseAttr); ok { 106 ret.ProviderConfig.Type = NewLegacyProvider(tt.Name) 107 } else { 108 diags = diags.Append(&hcl.Diagnostic{ 109 Severity: hcl.DiagError, 110 Summary: "Invalid provider configuration address", 111 Detail: "The prefix \"provider.\" must be followed by a provider type name.", 112 Subject: remain[1].SourceRange().Ptr(), 113 }) 114 return ret, diags 115 } 116 117 if len(remain) == 3 { 118 if tt, ok := remain[2].(hcl.TraverseAttr); ok { 119 ret.ProviderConfig.Alias = tt.Name 120 } else { 121 diags = diags.Append(&hcl.Diagnostic{ 122 Severity: hcl.DiagError, 123 Summary: "Invalid provider configuration address", 124 Detail: "Provider type name must be followed by a configuration alias name.", 125 Subject: remain[2].SourceRange().Ptr(), 126 }) 127 return ret, diags 128 } 129 } 130 131 return ret, diags 132 } 133 134 // ParseAbsProviderConfigStr is a helper wrapper around ParseAbsProviderConfig 135 // that takes a string and parses it with the HCL native syntax traversal parser 136 // before interpreting it. 137 // 138 // This should be used only in specialized situations since it will cause the 139 // created references to not have any meaningful source location information. 140 // If a reference string is coming from a source that should be identified in 141 // error messages then the caller should instead parse it directly using a 142 // suitable function from the HCL API and pass the traversal itself to 143 // ParseAbsProviderConfig. 144 // 145 // Error diagnostics are returned if either the parsing fails or the analysis 146 // of the traversal fails. There is no way for the caller to distinguish the 147 // two kinds of diagnostics programmatically. If error diagnostics are returned 148 // the returned address is invalid. 149 func ParseAbsProviderConfigStr(str string) (AbsProviderConfig, tfdiags.Diagnostics) { 150 var diags tfdiags.Diagnostics 151 152 traversal, parseDiags := hclsyntax.ParseTraversalAbs([]byte(str), "", hcl.Pos{Line: 1, Column: 1}) 153 diags = diags.Append(parseDiags) 154 if parseDiags.HasErrors() { 155 return AbsProviderConfig{}, diags 156 } 157 158 addr, addrDiags := ParseAbsProviderConfig(traversal) 159 diags = diags.Append(addrDiags) 160 return addr, diags 161 } 162 163 // ProviderConfigDefault returns the address of the default provider config 164 // of the given type inside the recieving module instance. 165 func (m ModuleInstance) ProviderConfigDefault(name string) AbsProviderConfig { 166 return AbsProviderConfig{ 167 Module: m, 168 ProviderConfig: ProviderConfig{ 169 Type: NewLegacyProvider(name), 170 }, 171 } 172 } 173 174 // ProviderConfigAliased returns the address of an aliased provider config 175 // of with given type and alias inside the recieving module instance. 176 func (m ModuleInstance) ProviderConfigAliased(name, alias string) AbsProviderConfig { 177 return AbsProviderConfig{ 178 Module: m, 179 ProviderConfig: ProviderConfig{ 180 Type: NewLegacyProvider(name), 181 Alias: alias, 182 }, 183 } 184 } 185 186 // Inherited returns an address that the receiving configuration address might 187 // inherit from in a parent module. The second bool return value indicates if 188 // such inheritance is possible, and thus whether the returned address is valid. 189 // 190 // Inheritance is possible only for default (un-aliased) providers in modules 191 // other than the root module. Even if a valid address is returned, inheritence 192 // may not be performed for other reasons, such as if the calling module 193 // provided explicit provider configurations within the call for this module. 194 // The ProviderTransformer graph transform in the main terraform module has 195 // the authoritative logic for provider inheritance, and this method is here 196 // mainly just for its benefit. 197 func (pc AbsProviderConfig) Inherited() (AbsProviderConfig, bool) { 198 // Can't inherit if we're already in the root. 199 if len(pc.Module) == 0 { 200 return AbsProviderConfig{}, false 201 } 202 203 // Can't inherit if we have an alias. 204 if pc.ProviderConfig.Alias != "" { 205 return AbsProviderConfig{}, false 206 } 207 208 // Otherwise, we might inherit from a configuration with the same 209 // provider name in the parent module instance. 210 parentMod := pc.Module.Parent() 211 return pc.ProviderConfig.Absolute(parentMod), true 212 } 213 214 func (pc AbsProviderConfig) String() string { 215 if len(pc.Module) == 0 { 216 return pc.ProviderConfig.String() 217 } 218 return fmt.Sprintf("%s.%s", pc.Module.String(), pc.ProviderConfig.String()) 219 }