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