github.com/pulumi/terraform@v1.4.0/pkg/addrs/provider.go (about) 1 package addrs 2 3 import ( 4 "github.com/hashicorp/hcl/v2" 5 tfaddr "github.com/hashicorp/terraform-registry-address" 6 svchost "github.com/hashicorp/terraform-svchost" 7 "github.com/pulumi/terraform/pkg/tfdiags" 8 ) 9 10 // Provider encapsulates a single provider type. In the future this will be 11 // extended to include additional fields including Namespace and SourceHost 12 type Provider = tfaddr.Provider 13 14 // DefaultProviderRegistryHost is the hostname used for provider addresses that do 15 // not have an explicit hostname. 16 const DefaultProviderRegistryHost = tfaddr.DefaultProviderRegistryHost 17 18 // BuiltInProviderHost is the pseudo-hostname used for the "built-in" provider 19 // namespace. Built-in provider addresses must also have their namespace set 20 // to BuiltInProviderNamespace in order to be considered as built-in. 21 const BuiltInProviderHost = tfaddr.BuiltInProviderHost 22 23 // BuiltInProviderNamespace is the provider namespace used for "built-in" 24 // providers. Built-in provider addresses must also have their hostname 25 // set to BuiltInProviderHost in order to be considered as built-in. 26 // 27 // The this namespace is literally named "builtin", in the hope that users 28 // who see FQNs containing this will be able to infer the way in which they are 29 // special, even if they haven't encountered the concept formally yet. 30 const BuiltInProviderNamespace = tfaddr.BuiltInProviderNamespace 31 32 // LegacyProviderNamespace is the special string used in the Namespace field 33 // of type Provider to mark a legacy provider address. This special namespace 34 // value would normally be invalid, and can be used only when the hostname is 35 // DefaultRegistryHost because that host owns the mapping from legacy name to 36 // FQN. 37 const LegacyProviderNamespace = tfaddr.LegacyProviderNamespace 38 39 func IsDefaultProvider(addr Provider) bool { 40 return addr.Hostname == DefaultProviderRegistryHost && addr.Namespace == "hashicorp" 41 } 42 43 // NewProvider constructs a provider address from its parts, and normalizes 44 // the namespace and type parts to lowercase using unicode case folding rules 45 // so that resulting addrs.Provider values can be compared using standard 46 // Go equality rules (==). 47 // 48 // The hostname is given as a svchost.Hostname, which is required by the 49 // contract of that type to have already been normalized for equality testing. 50 // 51 // This function will panic if the given namespace or type name are not valid. 52 // When accepting namespace or type values from outside the program, use 53 // ParseProviderPart first to check that the given value is valid. 54 func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider { 55 return tfaddr.NewProvider(hostname, namespace, typeName) 56 } 57 58 // ImpliedProviderForUnqualifiedType represents the rules for inferring what 59 // provider FQN a user intended when only a naked type name is available. 60 // 61 // For all except the type name "terraform" this returns a so-called "default" 62 // provider, which is under the registry.terraform.io/hashicorp/ namespace. 63 // 64 // As a special case, the string "terraform" maps to 65 // "terraform.io/builtin/terraform" because that is the more likely user 66 // intent than the now-unmaintained "registry.terraform.io/hashicorp/terraform" 67 // which remains only for compatibility with older Terraform versions. 68 func ImpliedProviderForUnqualifiedType(typeName string) Provider { 69 switch typeName { 70 case "terraform": 71 // Note for future maintainers: any additional strings we add here 72 // as implied to be builtin must never also be use as provider names 73 // in the registry.terraform.io/hashicorp/... namespace, because 74 // otherwise older versions of Terraform could implicitly select 75 // the registry name instead of the internal one. 76 return NewBuiltInProvider(typeName) 77 default: 78 return NewDefaultProvider(typeName) 79 } 80 } 81 82 // NewDefaultProvider returns the default address of a HashiCorp-maintained, 83 // Registry-hosted provider. 84 func NewDefaultProvider(name string) Provider { 85 return tfaddr.Provider{ 86 Type: MustParseProviderPart(name), 87 Namespace: "hashicorp", 88 Hostname: DefaultProviderRegistryHost, 89 } 90 } 91 92 // NewBuiltInProvider returns the address of a "built-in" provider. See 93 // the docs for Provider.IsBuiltIn for more information. 94 func NewBuiltInProvider(name string) Provider { 95 return tfaddr.Provider{ 96 Type: MustParseProviderPart(name), 97 Namespace: BuiltInProviderNamespace, 98 Hostname: BuiltInProviderHost, 99 } 100 } 101 102 // NewLegacyProvider returns a mock address for a provider. 103 // This will be removed when ProviderType is fully integrated. 104 func NewLegacyProvider(name string) Provider { 105 return Provider{ 106 // We intentionally don't normalize and validate the legacy names, 107 // because existing code expects legacy provider names to pass through 108 // verbatim, even if not compliant with our new naming rules. 109 Type: name, 110 Namespace: LegacyProviderNamespace, 111 Hostname: DefaultProviderRegistryHost, 112 } 113 } 114 115 // ParseProviderSourceString parses a value of the form expected in the "source" 116 // argument of a required_providers entry and returns the corresponding 117 // fully-qualified provider address. This is intended primarily to parse the 118 // FQN-like strings returned by terraform-config-inspect. 119 // 120 // The following are valid source string formats: 121 // 122 // - name 123 // - namespace/name 124 // - hostname/namespace/name 125 func ParseProviderSourceString(str string) (tfaddr.Provider, tfdiags.Diagnostics) { 126 var diags tfdiags.Diagnostics 127 128 ret, err := tfaddr.ParseProviderSource(str) 129 if pe, ok := err.(*tfaddr.ParserError); ok { 130 diags = diags.Append(&hcl.Diagnostic{ 131 Severity: hcl.DiagError, 132 Summary: pe.Summary, 133 Detail: pe.Detail, 134 }) 135 return ret, diags 136 } 137 138 if !ret.HasKnownNamespace() { 139 ret.Namespace = "hashicorp" 140 } 141 142 return ret, nil 143 } 144 145 // MustParseProviderSourceString is a wrapper around ParseProviderSourceString that panics if 146 // it returns an error. 147 func MustParseProviderSourceString(str string) Provider { 148 result, diags := ParseProviderSourceString(str) 149 if diags.HasErrors() { 150 panic(diags.Err().Error()) 151 } 152 return result 153 } 154 155 // ParseProviderPart processes an addrs.Provider namespace or type string 156 // provided by an end-user, producing a normalized version if possible or 157 // an error if the string contains invalid characters. 158 // 159 // A provider part is processed in the same way as an individual label in a DNS 160 // domain name: it is transformed to lowercase per the usual DNS case mapping 161 // and normalization rules and may contain only letters, digits, and dashes. 162 // Additionally, dashes may not appear at the start or end of the string. 163 // 164 // These restrictions are intended to allow these names to appear in fussy 165 // contexts such as directory/file names on case-insensitive filesystems, 166 // repository names on GitHub, etc. We're using the DNS rules in particular, 167 // rather than some similar rules defined locally, because the hostname part 168 // of an addrs.Provider is already a hostname and it's ideal to use exactly 169 // the same case folding and normalization rules for all of the parts. 170 // 171 // In practice a provider type string conventionally does not contain dashes 172 // either. Such names are permitted, but providers with such type names will be 173 // hard to use because their resource type names will not be able to contain 174 // the provider type name and thus each resource will need an explicit provider 175 // address specified. (A real-world example of such a provider is the 176 // "google-beta" variant of the GCP provider, which has resource types that 177 // start with the "google_" prefix instead.) 178 // 179 // It's valid to pass the result of this function as the argument to a 180 // subsequent call, in which case the result will be identical. 181 func ParseProviderPart(given string) (string, error) { 182 return tfaddr.ParseProviderPart(given) 183 } 184 185 // MustParseProviderPart is a wrapper around ParseProviderPart that panics if 186 // it returns an error. 187 func MustParseProviderPart(given string) string { 188 result, err := ParseProviderPart(given) 189 if err != nil { 190 panic(err.Error()) 191 } 192 return result 193 } 194 195 // IsProviderPartNormalized compares a given string to the result of ParseProviderPart(string) 196 func IsProviderPartNormalized(str string) (bool, error) { 197 normalized, err := ParseProviderPart(str) 198 if err != nil { 199 return false, err 200 } 201 if str == normalized { 202 return true, nil 203 } 204 return false, nil 205 }