From 7feef1c4aa5b408989e945fb83bfa65b631081f7 Mon Sep 17 00:00:00 2001 From: Radek Simko Date: Fri, 8 Jul 2022 14:46:29 +0100 Subject: [PATCH] Use hashicorp/terraform-registry-address as a decoupled library (#28338) * refactor: Use tfaddr for provider address parsing * refactor: Use tfaddr for module address parsing * deps: introduce hashicorp/terraform-registry-address --- go.mod | 4 +- go.sum | 5 +- internal/addrs/module_package.go | 47 +--- internal/addrs/module_source.go | 173 ++---------- internal/addrs/module_source_test.go | 82 +++--- internal/addrs/provider.go | 301 ++------------------- internal/addrs/provider_test.go | 4 +- internal/command/test.go | 2 +- internal/configs/module_call_test.go | 4 +- internal/configs/provider_validation.go | 4 +- internal/getproviders/didyoumean.go | 2 +- internal/initwd/module_install.go | 14 +- internal/refactoring/move_validate_test.go | 2 +- internal/registry/regsrc/module.go | 2 +- 14 files changed, 104 insertions(+), 542 deletions(-) diff --git a/go.mod b/go.mod index 94090cc92eca..e603cd907ce4 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/dylanmei/winrmtest v0.0.0-20210303004826-fbc9ae56efb6 github.com/go-test/deep v1.0.3 github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.5.6 + github.com/google/go-cmp v0.5.8 github.com/google/uuid v1.2.0 github.com/gophercloud/gophercloud v0.10.1-0.20200424014253-c3bfe50899e5 github.com/gophercloud/utils v0.0.0-20200423144003-7c72efc7435d @@ -46,6 +46,7 @@ require ( github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f github.com/hashicorp/hcl/v2 v2.13.0 github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 + github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 github.com/jmespath/go-jmespath v0.4.0 github.com/joyent/triton-go v0.0.0-20180313100802-d8f9c0314926 @@ -177,7 +178,6 @@ require ( golang.org/x/exp/typeparams v0.0.0-20220218215828-6cf2b201936e // indirect golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect golang.org/x/time v0.0.0-20220224211638-0e9765cccd65 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect diff --git a/go.sum b/go.sum index e315aaf80a9d..15b93d78261e 100644 --- a/go.sum +++ b/go.sum @@ -282,8 +282,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -403,6 +404,8 @@ github.com/hashicorp/serf v0.9.5 h1:EBWvyu9tcRszt3Bxp3KNssBMP1KuHWyO51lz9+786iM= github.com/hashicorp/serf v0.9.5/go.mod h1:UWDWwZeL5cuWDJdl0C6wrvrUwEqtQ4ZKBKKENpqIUyk= github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2 h1:l+bLFvHjqtgNQwWxwrFX9PemGAAO2P1AGZM7zlMNvCs= github.com/hashicorp/terraform-config-inspect v0.0.0-20210209133302-4fd17a0faac2/go.mod h1:Z0Nnk4+3Cy89smEbrq+sl1bxc9198gIP4I7wcQF6Kqs= +github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c h1:D8aRO6+mTqHfLsK/BC3j5OAoogv1WLRWzY1AaTo3rBg= +github.com/hashicorp/terraform-registry-address v0.0.0-20220623143253-7d51757b572c/go.mod h1:Wn3Na71knbXc1G8Lh+yu/dQWWJeFQEpDeJMtWMtlmNI= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= diff --git a/internal/addrs/module_package.go b/internal/addrs/module_package.go index dc5a6621c798..e1c82e36ed7a 100644 --- a/internal/addrs/module_package.go +++ b/internal/addrs/module_package.go @@ -1,9 +1,7 @@ package addrs import ( - "strings" - - svchost "github.com/hashicorp/terraform-svchost" + tfaddr "github.com/hashicorp/terraform-registry-address" ) // A ModulePackage represents a physical location where Terraform can retrieve @@ -45,45 +43,4 @@ func (p ModulePackage) String() string { // registry in order to find a real module package address. These being // distinct is intended to help future maintainers more easily follow the // series of steps in the module installer, with the help of the type checker. -type ModuleRegistryPackage struct { - Host svchost.Hostname - Namespace string - Name string - TargetSystem string -} - -func (s ModuleRegistryPackage) String() string { - var buf strings.Builder - // Note: we're using the "display" form of the hostname here because - // for our service hostnames "for display" means something different: - // it means to render non-ASCII characters directly as Unicode - // characters, rather than using the "punycode" representation we - // use for internal processing, and so the "display" representation - // is actually what users would write in their configurations. - return s.Host.ForDisplay() + "/" + s.ForRegistryProtocol() - return buf.String() -} - -func (s ModuleRegistryPackage) ForDisplay() string { - if s.Host == DefaultModuleRegistryHost { - return s.ForRegistryProtocol() - } - return s.Host.ForDisplay() + "/" + s.ForRegistryProtocol() -} - -// ForRegistryProtocol returns a string representation of just the namespace, -// name, and target system portions of the address, always omitting the -// registry hostname and the subdirectory portion, if any. -// -// This is primarily intended for generating addresses to send to the -// registry in question via the registry protocol, since the protocol -// skips sending the registry its own hostname as part of identifiers. -func (s ModuleRegistryPackage) ForRegistryProtocol() string { - var buf strings.Builder - buf.WriteString(s.Namespace) - buf.WriteByte('/') - buf.WriteString(s.Name) - buf.WriteByte('/') - buf.WriteString(s.TargetSystem) - return buf.String() -} +type ModuleRegistryPackage = tfaddr.ModulePackage diff --git a/internal/addrs/module_source.go b/internal/addrs/module_source.go index c42bc9f04459..905b77e2f5c1 100644 --- a/internal/addrs/module_source.go +++ b/internal/addrs/module_source.go @@ -3,10 +3,9 @@ package addrs import ( "fmt" "path" - "regexp" "strings" - svchost "github.com/hashicorp/terraform-svchost" + tfaddr "github.com/hashicorp/terraform-registry-address" "github.com/hashicorp/terraform/internal/getmodules" ) @@ -197,30 +196,11 @@ func (s ModuleSourceLocal) ForDisplay() string { // combination of a ModuleSourceRegistry and a module version number into // a concrete ModuleSourceRemote that Terraform will then download and // install. -type ModuleSourceRegistry struct { - // PackageAddr is the registry package that the target module belongs to. - // The module installer must translate this into a ModuleSourceRemote - // using the registry API and then take that underlying address's - // PackageAddr in order to find the actual package location. - PackageAddr ModuleRegistryPackage - - // If Subdir is non-empty then it represents a sub-directory within the - // remote package that the registry address eventually resolves to. - // This will ultimately become the suffix of the Subdir of the - // ModuleSourceRemote that the registry address translates to. - // - // Subdir uses a normalized forward-slash-based path syntax within the - // virtual filesystem represented by the final package. It will never - // include `../` or `./` sequences. - Subdir string -} +type ModuleSourceRegistry tfaddr.Module // DefaultModuleRegistryHost is the hostname used for registry-based module // source addresses that do not have an explicit hostname. -const DefaultModuleRegistryHost = svchost.Hostname("registry.terraform.io") - -var moduleRegistryNamePattern = regexp.MustCompile("^[0-9A-Za-z](?:[0-9A-Za-z-_]{0,62}[0-9A-Za-z])?$") -var moduleRegistryTargetSystemPattern = regexp.MustCompile("^[0-9a-z]{1,64}$") +const DefaultModuleRegistryHost = tfaddr.DefaultModuleRegistryHost // ParseModuleSourceRegistry is a variant of ParseModuleSource which only // accepts module registry addresses, and will reject any other address type. @@ -237,147 +217,30 @@ func ParseModuleSourceRegistry(raw string) (ModuleSource, error) { return ModuleSourceRegistry{}, fmt.Errorf("can't use local directory %q as a module registry address", raw) } - ret, err := parseModuleSourceRegistry(raw) + src, err := tfaddr.ParseModuleSource(raw) if err != nil { - // This is to make sure we return a nil ModuleSource, rather than - // a non-nil ModuleSource containing a zero-value ModuleSourceRegistry. return nil, err } - return ret, nil -} - -func parseModuleSourceRegistry(raw string) (ModuleSourceRegistry, error) { - var err error - - var subDir string - raw, subDir = getmodules.SplitPackageSubdir(raw) - if strings.HasPrefix(subDir, "../") { - return ModuleSourceRegistry{}, fmt.Errorf("subdirectory path %q leads outside of the module package", subDir) - } - - parts := strings.Split(raw, "/") - // A valid registry address has either three or four parts, because the - // leading hostname part is optional. - if len(parts) != 3 && len(parts) != 4 { - return ModuleSourceRegistry{}, fmt.Errorf("a module registry source address must have either three or four slash-separated components") - } - - host := DefaultModuleRegistryHost - if len(parts) == 4 { - host, err = svchost.ForComparison(parts[0]) - if err != nil { - // The svchost library doesn't produce very good error messages to - // return to an end-user, so we'll use some custom ones here. - switch { - case strings.Contains(parts[0], "--"): - // Looks like possibly punycode, which we don't allow here - // to ensure that source addresses are written readably. - return ModuleSourceRegistry{}, fmt.Errorf("invalid module registry hostname %q; internationalized domain names must be given as direct unicode characters, not in punycode", parts[0]) - default: - return ModuleSourceRegistry{}, fmt.Errorf("invalid module registry hostname %q", parts[0]) - } - } - if !strings.Contains(host.String(), ".") { - return ModuleSourceRegistry{}, fmt.Errorf("invalid module registry hostname: must contain at least one dot") - } - // Discard the hostname prefix now that we've processed it - parts = parts[1:] - } - - ret := ModuleSourceRegistry{ - PackageAddr: ModuleRegistryPackage{ - Host: host, - }, - - Subdir: subDir, - } - - if host == svchost.Hostname("github.com") || host == svchost.Hostname("bitbucket.org") { - return ret, fmt.Errorf("can't use %q as a module registry host, because it's reserved for installing directly from version control repositories", host) - } - - if ret.PackageAddr.Namespace, err = parseModuleRegistryName(parts[0]); err != nil { - if strings.Contains(parts[0], ".") { - // Seems like the user omitted one of the latter components in - // an address with an explicit hostname. - return ret, fmt.Errorf("source address must have three more components after the hostname: the namespace, the name, and the target system") - } - return ret, fmt.Errorf("invalid namespace %q: %s", parts[0], err) - } - if ret.PackageAddr.Name, err = parseModuleRegistryName(parts[1]); err != nil { - return ret, fmt.Errorf("invalid module name %q: %s", parts[1], err) - } - if ret.PackageAddr.TargetSystem, err = parseModuleRegistryTargetSystem(parts[2]); err != nil { - if strings.Contains(parts[2], "?") { - // The user was trying to include a query string, probably? - return ret, fmt.Errorf("module registry addresses may not include a query string portion") - } - return ret, fmt.Errorf("invalid target system %q: %s", parts[2], err) - } - - return ret, nil -} - -// parseModuleRegistryName validates and normalizes a string in either the -// "namespace" or "name" position of a module registry source address. -func parseModuleRegistryName(given string) (string, error) { - // Similar to the names in provider source addresses, we defined these - // to be compatible with what filesystems and typical remote systems - // like GitHub allow in names. Unfortunately we didn't end up defining - // these exactly equivalently: provider names can only use dashes as - // punctuation, whereas module names can use underscores. So here we're - // using some regular expressions from the original module source - // implementation, rather than using the IDNA rules as we do in - // ParseProviderPart. - - if !moduleRegistryNamePattern.MatchString(given) { - return "", fmt.Errorf("must be between one and 64 characters, including ASCII letters, digits, dashes, and underscores, where dashes and underscores may not be the prefix or suffix") - } - - // We also skip normalizing the name to lowercase, because we historically - // didn't do that and so existing module registries might be doing - // case-sensitive matching. - return given, nil -} - -// parseModuleRegistryTargetSystem validates and normalizes a string in the -// "target system" position of a module registry source address. This is -// what we historically called "provider" but never actually enforced as -// being a provider address, and now _cannot_ be a provider address because -// provider addresses have three slash-separated components of their own. -func parseModuleRegistryTargetSystem(given string) (string, error) { - // Similar to the names in provider source addresses, we defined these - // to be compatible with what filesystems and typical remote systems - // like GitHub allow in names. Unfortunately we didn't end up defining - // these exactly equivalently: provider names can't use dashes or - // underscores. So here we're using some regular expressions from the - // original module source implementation, rather than using the IDNA rules - // as we do in ParseProviderPart. - - if !moduleRegistryTargetSystemPattern.MatchString(given) { - return "", fmt.Errorf("must be between one and 64 ASCII letters or digits") - } - - // We also skip normalizing the name to lowercase, because we historically - // didn't do that and so existing module registries might be doing - // case-sensitive matching. - return given, nil + return ModuleSourceRegistry{ + Package: src.Package, + Subdir: src.Subdir, + }, nil } func (s ModuleSourceRegistry) moduleSource() {} func (s ModuleSourceRegistry) String() string { if s.Subdir != "" { - return s.PackageAddr.String() + "//" + s.Subdir + return s.Package.String() + "//" + s.Subdir } - return s.PackageAddr.String() + return s.Package.String() } func (s ModuleSourceRegistry) ForDisplay() string { if s.Subdir != "" { - return s.PackageAddr.ForDisplay() + "//" + s.Subdir + return s.Package.ForDisplay() + "//" + s.Subdir } - return s.PackageAddr.ForDisplay() + return s.Package.ForDisplay() } // ModuleSourceRemote is a ModuleSource representing a remote location from @@ -387,9 +250,9 @@ func (s ModuleSourceRegistry) ForDisplay() string { // means that it's selecting a sub-directory of the given package to use as // the entry point into the package. type ModuleSourceRemote struct { - // PackageAddr is the address of the remote package that the requested + // Package is the address of the remote package that the requested // module belongs to. - PackageAddr ModulePackage + Package ModulePackage // If Subdir is non-empty then it represents a sub-directory within the // remote package which will serve as the entry-point for the package. @@ -445,8 +308,8 @@ func parseModuleSourceRemote(raw string) (ModuleSourceRemote, error) { } return ModuleSourceRemote{ - PackageAddr: ModulePackage(norm), - Subdir: subDir, + Package: ModulePackage(norm), + Subdir: subDir, }, nil } @@ -454,9 +317,9 @@ func (s ModuleSourceRemote) moduleSource() {} func (s ModuleSourceRemote) String() string { if s.Subdir != "" { - return s.PackageAddr.String() + "//" + s.Subdir + return s.Package.String() + "//" + s.Subdir } - return s.PackageAddr.String() + return s.Package.String() } func (s ModuleSourceRemote) ForDisplay() string { diff --git a/internal/addrs/module_source_test.go b/internal/addrs/module_source_test.go index 9604c33744bd..d6b5626ec682 100644 --- a/internal/addrs/module_source_test.go +++ b/internal/addrs/module_source_test.go @@ -59,7 +59,7 @@ func TestParseModuleSource(t *testing.T) { "main registry implied": { input: "hashicorp/subnets/cidr", want: ModuleSourceRegistry{ - PackageAddr: ModuleRegistryPackage{ + Package: ModuleRegistryPackage{ Host: svchost.Hostname("registry.terraform.io"), Namespace: "hashicorp", Name: "subnets", @@ -71,7 +71,7 @@ func TestParseModuleSource(t *testing.T) { "main registry implied, subdir": { input: "hashicorp/subnets/cidr//examples/foo", want: ModuleSourceRegistry{ - PackageAddr: ModuleRegistryPackage{ + Package: ModuleRegistryPackage{ Host: svchost.Hostname("registry.terraform.io"), Namespace: "hashicorp", Name: "subnets", @@ -92,7 +92,7 @@ func TestParseModuleSource(t *testing.T) { "custom registry": { input: "example.com/awesomecorp/network/happycloud", want: ModuleSourceRegistry{ - PackageAddr: ModuleRegistryPackage{ + Package: ModuleRegistryPackage{ Host: svchost.Hostname("example.com"), Namespace: "awesomecorp", Name: "network", @@ -104,7 +104,7 @@ func TestParseModuleSource(t *testing.T) { "custom registry, subdir": { input: "example.com/awesomecorp/network/happycloud//examples/foo", want: ModuleSourceRegistry{ - PackageAddr: ModuleRegistryPackage{ + Package: ModuleRegistryPackage{ Host: svchost.Hostname("example.com"), Namespace: "awesomecorp", Name: "network", @@ -118,68 +118,68 @@ func TestParseModuleSource(t *testing.T) { "github.com shorthand": { input: "github.com/hashicorp/terraform-cidr-subnets", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), + Package: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), }, }, "github.com shorthand, subdir": { input: "github.com/hashicorp/terraform-cidr-subnets//example/foo", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), - Subdir: "example/foo", + Package: ModulePackage("git::https://github.com/hashicorp/terraform-cidr-subnets.git"), + Subdir: "example/foo", }, }, "git protocol, URL-style": { input: "git://example.com/code/baz.git", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git://example.com/code/baz.git"), + Package: ModulePackage("git://example.com/code/baz.git"), }, }, "git protocol, URL-style, subdir": { input: "git://example.com/code/baz.git//bleep/bloop", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git://example.com/code/baz.git"), - Subdir: "bleep/bloop", + Package: ModulePackage("git://example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, "git over HTTPS, URL-style": { input: "git::https://example.com/code/baz.git", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git::https://example.com/code/baz.git"), + Package: ModulePackage("git::https://example.com/code/baz.git"), }, }, "git over HTTPS, URL-style, subdir": { input: "git::https://example.com/code/baz.git//bleep/bloop", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git::https://example.com/code/baz.git"), - Subdir: "bleep/bloop", + Package: ModulePackage("git::https://example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, "git over SSH, URL-style": { input: "git::ssh://git@example.com/code/baz.git", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), + Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), }, }, "git over SSH, URL-style, subdir": { input: "git::ssh://git@example.com/code/baz.git//bleep/bloop", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), - Subdir: "bleep/bloop", + Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, "git over SSH, scp-style": { input: "git::git@example.com:code/baz.git", want: ModuleSourceRemote{ // Normalized to URL-style - PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), + Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), }, }, "git over SSH, scp-style, subdir": { input: "git::git@example.com:code/baz.git//bleep/bloop", want: ModuleSourceRemote{ // Normalized to URL-style - PackageAddr: ModulePackage("git::ssh://git@example.com/code/baz.git"), - Subdir: "bleep/bloop", + Package: ModulePackage("git::ssh://git@example.com/code/baz.git"), + Subdir: "bleep/bloop", }, }, @@ -190,63 +190,63 @@ func TestParseModuleSource(t *testing.T) { "Google Cloud Storage bucket implied, path prefix": { input: "www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), + Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), }, }, "Google Cloud Storage bucket, path prefix": { input: "gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), + Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH_TO_MODULE"), }, }, "Google Cloud Storage bucket implied, archive object": { input: "www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), + Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), }, }, "Google Cloud Storage bucket, archive object": { input: "gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), + Package: ModulePackage("gcs::https://www.googleapis.com/storage/v1/BUCKET_NAME/PATH/TO/module.zip"), }, }, "Amazon S3 bucket implied, archive object": { input: "s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), + Package: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), }, }, "Amazon S3 bucket, archive object": { input: "s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), + Package: ModulePackage("s3::https://s3-eu-west-1.amazonaws.com/examplecorp-terraform-modules/vpc.zip"), }, }, "HTTP URL": { input: "http://example.com/module", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("http://example.com/module"), + Package: ModulePackage("http://example.com/module"), }, }, "HTTPS URL": { input: "https://example.com/module", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("https://example.com/module"), + Package: ModulePackage("https://example.com/module"), }, }, "HTTPS URL, archive file": { input: "https://example.com/module.zip", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("https://example.com/module.zip"), + Package: ModulePackage("https://example.com/module.zip"), }, }, "HTTPS URL, forced archive file": { input: "https://example.com/module?archive=tar", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("https://example.com/module?archive=tar"), + Package: ModulePackage("https://example.com/module?archive=tar"), }, }, "HTTPS URL, forced archive file and checksum": { @@ -255,7 +255,7 @@ func TestParseModuleSource(t *testing.T) { // The query string only actually gets processed when we finally // do the get, so "checksum=blah" is accepted as valid up // at this parsing layer. - PackageAddr: ModulePackage("https://example.com/module?archive=tar&checksum=blah"), + Package: ModulePackage("https://example.com/module?archive=tar&checksum=blah"), }, }, @@ -266,7 +266,7 @@ func TestParseModuleSource(t *testing.T) { // is replaced by a deep filesystem copy instead. input: "/tmp/foo/example", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("file:///tmp/foo/example"), + Package: ModulePackage("file:///tmp/foo/example"), }, }, "absolute filesystem path, subdir": { @@ -277,8 +277,8 @@ func TestParseModuleSource(t *testing.T) { // syntax to move the package root higher in the real filesystem. input: "/tmp/foo//example", want: ModuleSourceRemote{ - PackageAddr: ModulePackage("file:///tmp/foo"), - Subdir: "example", + Package: ModulePackage("file:///tmp/foo"), + Subdir: "example", }, }, @@ -310,7 +310,7 @@ func TestParseModuleSource(t *testing.T) { // Unfortunately go-getter doesn't actually reject a totally // invalid address like this until getting time, as long as // it looks somewhat like a URL. - PackageAddr: ModulePackage("dfgdfgsd:dgfhdfghdfghdfg/dfghdfghdfg"), + Package: ModulePackage("dfgdfgsd:dgfhdfghdfghdfg/dfghdfghdfg"), }, }, } @@ -344,8 +344,8 @@ func TestParseModuleSource(t *testing.T) { func TestModuleSourceRemoteFromRegistry(t *testing.T) { t.Run("both have subdir", func(t *testing.T) { remote := ModuleSourceRemote{ - PackageAddr: ModulePackage("boop"), - Subdir: "foo", + Package: ModulePackage("boop"), + Subdir: "foo", } registry := ModuleSourceRegistry{ Subdir: "bar", @@ -363,8 +363,8 @@ func TestModuleSourceRemoteFromRegistry(t *testing.T) { }) t.Run("only remote has subdir", func(t *testing.T) { remote := ModuleSourceRemote{ - PackageAddr: ModulePackage("boop"), - Subdir: "foo", + Package: ModulePackage("boop"), + Subdir: "foo", } registry := ModuleSourceRegistry{ Subdir: "", @@ -382,8 +382,8 @@ func TestModuleSourceRemoteFromRegistry(t *testing.T) { }) t.Run("only registry has subdir", func(t *testing.T) { remote := ModuleSourceRemote{ - PackageAddr: ModulePackage("boop"), - Subdir: "", + Package: ModulePackage("boop"), + Subdir: "", } registry := ModuleSourceRegistry{ Subdir: "bar", @@ -565,7 +565,7 @@ func TestParseModuleSourceRegistry(t *testing.T) { if got, want := addr.ForDisplay(), test.wantForDisplay; got != want { t.Errorf("wrong ForDisplay() result\ngot: %s\nwant: %s", got, want) } - if got, want := addr.PackageAddr.ForRegistryProtocol(), test.wantForProtocol; got != want { + if got, want := addr.Package.ForRegistryProtocol(), test.wantForProtocol; got != want { t.Errorf("wrong ForRegistryProtocol() result\ngot: %s\nwant: %s", got, want) } }) diff --git a/internal/addrs/provider.go b/internal/addrs/provider.go index e3902c3d968c..e8ceaa9a1cf6 100644 --- a/internal/addrs/provider.go +++ b/internal/addrs/provider.go @@ -1,32 +1,24 @@ package addrs import ( - "fmt" - "strings" - - "golang.org/x/net/idna" - "github.com/hashicorp/hcl/v2" + tfaddr "github.com/hashicorp/terraform-registry-address" svchost "github.com/hashicorp/terraform-svchost" "github.com/hashicorp/terraform/internal/tfdiags" ) // Provider encapsulates a single provider type. In the future this will be // extended to include additional fields including Namespace and SourceHost -type Provider struct { - Type string - Namespace string - Hostname svchost.Hostname -} +type Provider = tfaddr.Provider // DefaultProviderRegistryHost is the hostname used for provider addresses that do // not have an explicit hostname. -const DefaultProviderRegistryHost = svchost.Hostname("registry.terraform.io") +const DefaultProviderRegistryHost = tfaddr.DefaultProviderRegistryHost // BuiltInProviderHost is the pseudo-hostname used for the "built-in" provider // namespace. Built-in provider addresses must also have their namespace set // to BuiltInProviderNamespace in order to be considered as built-in. -const BuiltInProviderHost = svchost.Hostname("terraform.io") +const BuiltInProviderHost = tfaddr.BuiltInProviderHost // BuiltInProviderNamespace is the provider namespace used for "built-in" // providers. Built-in provider addresses must also have their hostname @@ -35,34 +27,17 @@ const BuiltInProviderHost = svchost.Hostname("terraform.io") // The this namespace is literally named "builtin", in the hope that users // who see FQNs containing this will be able to infer the way in which they are // special, even if they haven't encountered the concept formally yet. -const BuiltInProviderNamespace = "builtin" +const BuiltInProviderNamespace = tfaddr.BuiltInProviderNamespace // LegacyProviderNamespace is the special string used in the Namespace field // of type Provider to mark a legacy provider address. This special namespace // value would normally be invalid, and can be used only when the hostname is // DefaultRegistryHost because that host owns the mapping from legacy name to // FQN. -const LegacyProviderNamespace = "-" +const LegacyProviderNamespace = tfaddr.LegacyProviderNamespace -// String returns an FQN string, indended for use in machine-readable output. -func (pt Provider) String() string { - if pt.IsZero() { - panic("called String on zero-value addrs.Provider") - } - return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type -} - -// ForDisplay returns a user-friendly FQN string, simplified for readability. If -// the provider is using the default hostname, the hostname is omitted. -func (pt Provider) ForDisplay() string { - if pt.IsZero() { - panic("called ForDisplay on zero-value addrs.Provider") - } - - if pt.Hostname == DefaultProviderRegistryHost { - return pt.Namespace + "/" + pt.Type - } - return pt.Hostname.ForDisplay() + "/" + pt.Namespace + "/" + pt.Type +func IsDefaultProvider(addr Provider) bool { + return addr.Hostname == DefaultProviderRegistryHost && addr.Namespace == "hashicorp" } // NewProvider constructs a provider address from its parts, and normalizes @@ -77,18 +52,7 @@ func (pt Provider) ForDisplay() string { // When accepting namespace or type values from outside the program, use // ParseProviderPart first to check that the given value is valid. func NewProvider(hostname svchost.Hostname, namespace, typeName string) Provider { - if namespace == LegacyProviderNamespace { - // Legacy provider addresses must always be created via - // NewLegacyProvider so that we can use static analysis to find - // codepaths still working with those. - panic("attempt to create legacy provider address using NewProvider; use NewLegacyProvider instead") - } - - return Provider{ - Type: MustParseProviderPart(typeName), - Namespace: MustParseProviderPart(namespace), - Hostname: hostname, - } + return tfaddr.NewProvider(hostname, namespace, typeName) } // ImpliedProviderForUnqualifiedType represents the rules for inferring what @@ -118,7 +82,7 @@ func ImpliedProviderForUnqualifiedType(typeName string) Provider { // NewDefaultProvider returns the default address of a HashiCorp-maintained, // Registry-hosted provider. func NewDefaultProvider(name string) Provider { - return Provider{ + return tfaddr.Provider{ Type: MustParseProviderPart(name), Namespace: "hashicorp", Hostname: DefaultProviderRegistryHost, @@ -128,7 +92,7 @@ func NewDefaultProvider(name string) Provider { // NewBuiltInProvider returns the address of a "built-in" provider. See // the docs for Provider.IsBuiltIn for more information. func NewBuiltInProvider(name string) Provider { - return Provider{ + return tfaddr.Provider{ Type: MustParseProviderPart(name), Namespace: BuiltInProviderNamespace, Hostname: BuiltInProviderHost, @@ -148,80 +112,6 @@ func NewLegacyProvider(name string) Provider { } } -// LegacyString returns the provider type, which is frequently used -// interchangeably with provider name. This function can and should be removed -// when provider type is fully integrated. As a safeguard for future -// refactoring, this function panics if the Provider is not a legacy provider. -func (pt Provider) LegacyString() string { - if pt.IsZero() { - panic("called LegacyString on zero-value addrs.Provider") - } - if pt.Namespace != LegacyProviderNamespace && pt.Namespace != BuiltInProviderNamespace { - panic(pt.String() + " cannot be represented as a legacy string") - } - return pt.Type -} - -// IsZero returns true if the receiver is the zero value of addrs.Provider. -// -// The zero value is not a valid addrs.Provider and calling other methods on -// such a value is likely to either panic or otherwise misbehave. -func (pt Provider) IsZero() bool { - return pt == Provider{} -} - -// IsBuiltIn returns true if the receiver is the address of a "built-in" -// provider. That is, a provider under terraform.io/builtin/ which is -// included as part of the Terraform binary itself rather than one to be -// installed from elsewhere. -// -// These are ignored by the provider installer because they are assumed to -// already be available without any further installation. -func (pt Provider) IsBuiltIn() bool { - return pt.Hostname == BuiltInProviderHost && pt.Namespace == BuiltInProviderNamespace -} - -// LessThan returns true if the receiver should sort before the other given -// address in an ordered list of provider addresses. -// -// This ordering is an arbitrary one just to allow deterministic results from -// functions that would otherwise have no natural ordering. It's subject -// to change in future. -func (pt Provider) LessThan(other Provider) bool { - switch { - case pt.Hostname != other.Hostname: - return pt.Hostname < other.Hostname - case pt.Namespace != other.Namespace: - return pt.Namespace < other.Namespace - default: - return pt.Type < other.Type - } -} - -// IsLegacy returns true if the provider is a legacy-style provider -func (pt Provider) IsLegacy() bool { - if pt.IsZero() { - panic("called IsLegacy() on zero-value addrs.Provider") - } - - return pt.Hostname == DefaultProviderRegistryHost && pt.Namespace == LegacyProviderNamespace - -} - -// IsDefault returns true if the provider is a default hashicorp provider -func (pt Provider) IsDefault() bool { - if pt.IsZero() { - panic("called IsDefault() on zero-value addrs.Provider") - } - - return pt.Hostname == DefaultProviderRegistryHost && pt.Namespace == "hashicorp" -} - -// Equals returns true if the receiver and other provider have the same attributes. -func (pt Provider) Equals(other Provider) bool { - return pt == other -} - // ParseProviderSourceString parses the source attribute and returns a provider. // This is intended primarily to parse the FQN-like strings returned by // terraform-config-inspect. @@ -230,146 +120,24 @@ func (pt Provider) Equals(other Provider) bool { // name // namespace/name // hostname/namespace/name -func ParseProviderSourceString(str string) (Provider, tfdiags.Diagnostics) { - var ret Provider +func ParseProviderSourceString(str string) (tfaddr.Provider, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - // split the source string into individual components - parts := strings.Split(str, "/") - if len(parts) == 0 || len(parts) > 3 { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider source string", - Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`, - }) - return ret, diags - } - - // check for an invalid empty string in any part - for i := range parts { - if parts[i] == "" { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider source string", - Detail: `The "source" attribute must be in the format "[hostname/][namespace/]name"`, - }) - return ret, diags - } - } - - // check the 'name' portion, which is always the last part - givenName := parts[len(parts)-1] - name, err := ParseProviderPart(givenName) - if err != nil { + ret, err := tfaddr.ParseProviderSource(str) + if pe, ok := err.(*tfaddr.ParserError); ok { diags = diags.Append(&hcl.Diagnostic{ Severity: hcl.DiagError, - Summary: "Invalid provider type", - Detail: fmt.Sprintf(`Invalid provider type %q in source %q: %s"`, givenName, str, err), + Summary: pe.Summary, + Detail: pe.Detail, }) return ret, diags } - ret.Type = name - ret.Hostname = DefaultProviderRegistryHost - - if len(parts) == 1 { - return NewDefaultProvider(parts[0]), diags - } - if len(parts) >= 2 { - // the namespace is always the second-to-last part - givenNamespace := parts[len(parts)-2] - if givenNamespace == LegacyProviderNamespace { - // For now we're tolerating legacy provider addresses until we've - // finished updating the rest of the codebase to no longer use them, - // or else we'd get errors round-tripping through legacy subsystems. - ret.Namespace = LegacyProviderNamespace - } else { - namespace, err := ParseProviderPart(givenNamespace) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider namespace", - Detail: fmt.Sprintf(`Invalid provider namespace %q in source %q: %s"`, namespace, str, err), - }) - return Provider{}, diags - } - ret.Namespace = namespace - } + if !ret.HasKnownNamespace() { + ret.Namespace = "hashicorp" } - // Final Case: 3 parts - if len(parts) == 3 { - // the namespace is always the first part in a three-part source string - hn, err := svchost.ForComparison(parts[0]) - if err != nil { - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider source hostname", - Detail: fmt.Sprintf(`Invalid provider source hostname namespace %q in source %q: %s"`, hn, str, err), - }) - return Provider{}, diags - } - ret.Hostname = hn - } - - if ret.Namespace == LegacyProviderNamespace && ret.Hostname != DefaultProviderRegistryHost { - // Legacy provider addresses must always be on the default registry - // host, because the default registry host decides what actual FQN - // each one maps to. - diags = diags.Append(&hcl.Diagnostic{ - Severity: hcl.DiagError, - Summary: "Invalid provider namespace", - Detail: "The legacy provider namespace \"-\" can be used only with hostname " + DefaultProviderRegistryHost.ForDisplay() + ".", - }) - return Provider{}, diags - } - - // Due to how plugin executables are named and provider git repositories - // are conventionally named, it's a reasonable and - // apparently-somewhat-common user error to incorrectly use the - // "terraform-provider-" prefix in a provider source address. There is - // no good reason for a provider to have the prefix "terraform-" anyway, - // so we've made that invalid from the start both so we can give feedback - // to provider developers about the terraform- prefix being redundant - // and give specialized feedback to folks who incorrectly use the full - // terraform-provider- prefix to help them self-correct. - const redundantPrefix = "terraform-" - const userErrorPrefix = "terraform-provider-" - if strings.HasPrefix(ret.Type, redundantPrefix) { - if strings.HasPrefix(ret.Type, userErrorPrefix) { - // Likely user error. We only return this specialized error if - // whatever is after the prefix would otherwise be a - // syntactically-valid provider type, so we don't end up advising - // the user to try something that would be invalid for another - // reason anyway. - // (This is mainly just for robustness, because the validation - // we already did above should've rejected most/all ways for - // the suggestedType to end up invalid here.) - suggestedType := ret.Type[len(userErrorPrefix):] - if _, err := ParseProviderPart(suggestedType); err == nil { - suggestedAddr := ret - suggestedAddr.Type = suggestedType - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid provider type", - fmt.Sprintf("Provider source %q has a type with the prefix %q, which isn't valid. Although that prefix is often used in the names of version control repositories for Terraform providers, provider source strings should not include it.\n\nDid you mean %q?", ret.ForDisplay(), userErrorPrefix, suggestedAddr.ForDisplay()), - )) - return Provider{}, diags - } - } - // Otherwise, probably instead an incorrectly-named provider, perhaps - // arising from a similar instinct to what causes there to be - // thousands of Python packages on PyPI with "python-"-prefixed - // names. - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Invalid provider type", - fmt.Sprintf("Provider source %q has a type with the prefix %q, which isn't allowed because it would be redundant to name a Terraform provider with that prefix. If you are the author of this provider, rename it to not include the prefix.", ret, redundantPrefix), - )) - return Provider{}, diags - } - - return ret, diags + return ret, nil } // MustParseProviderSourceString is a wrapper around ParseProviderSourceString that panics if @@ -409,36 +177,7 @@ func MustParseProviderSourceString(str string) Provider { // It's valid to pass the result of this function as the argument to a // subsequent call, in which case the result will be identical. func ParseProviderPart(given string) (string, error) { - if len(given) == 0 { - return "", fmt.Errorf("must have at least one character") - } - - // We're going to process the given name using the same "IDNA" library we - // use for the hostname portion, since it already implements the case - // folding rules we want. - // - // The idna library doesn't expose individual label parsing directly, but - // once we've verified it doesn't contain any dots we can just treat it - // like a top-level domain for this library's purposes. - if strings.ContainsRune(given, '.') { - return "", fmt.Errorf("dots are not allowed") - } - - // We don't allow names containing multiple consecutive dashes, just as - // a matter of preference: they look weird, confusing, or incorrect. - // This also, as a side-effect, prevents the use of the "punycode" - // indicator prefix "xn--" that would cause the IDNA library to interpret - // the given name as punycode, because that would be weird and unexpected. - if strings.Contains(given, "--") { - return "", fmt.Errorf("cannot use multiple consecutive dashes") - } - - result, err := idna.Lookup.ToUnicode(given) - if err != nil { - return "", fmt.Errorf("must contain only letters, digits, and dashes, and may not use leading or trailing dashes") - } - - return result, nil + return tfaddr.ParseProviderPart(given) } // MustParseProviderPart is a wrapper around ParseProviderPart that panics if diff --git a/internal/addrs/provider_test.go b/internal/addrs/provider_test.go index 40361058cbdc..0f50475736aa 100644 --- a/internal/addrs/provider_test.go +++ b/internal/addrs/provider_test.go @@ -124,7 +124,7 @@ func TestProviderDisplay(t *testing.T) { } } -func TestProviderIsDefault(t *testing.T) { +func TestProviderIsDefaultProvider(t *testing.T) { tests := []struct { Input Provider Want bool @@ -156,7 +156,7 @@ func TestProviderIsDefault(t *testing.T) { } for _, test := range tests { - got := test.Input.IsDefault() + got := IsDefaultProvider(test.Input) if got != test.Want { t.Errorf("wrong result for %s\n", test.Input.String()) } diff --git a/internal/command/test.go b/internal/command/test.go index fc6d7ef70047..1f18689f1bc5 100644 --- a/internal/command/test.go +++ b/internal/command/test.go @@ -317,7 +317,7 @@ func (c *TestCommand) prepareSuiteDir(ctx context.Context, suiteName string) (te locks := depsfile.NewLocks() evts := &providercache.InstallerEvents{ QueryPackagesFailure: func(provider addrs.Provider, err error) { - if err != nil && provider.IsDefault() && provider.Type == "test" { + if err != nil && addrs.IsDefaultProvider(provider) && provider.Type == "test" { // This is some additional context for the failure error // we'll generate afterwards. Not the most ideal UX but // good enough for this prototype implementation, to help diff --git a/internal/configs/module_call_test.go b/internal/configs/module_call_test.go index 9c607f8e34c3..af2269dca0bb 100644 --- a/internal/configs/module_call_test.go +++ b/internal/configs/module_call_test.go @@ -45,7 +45,7 @@ func TestLoadModuleCall(t *testing.T) { { Name: "bar", SourceAddr: addrs.ModuleSourceRegistry{ - PackageAddr: addrs.ModuleRegistryPackage{ + Package: addrs.ModuleRegistryPackage{ Host: addrs.DefaultModuleRegistryHost, Namespace: "hashicorp", Name: "bar", @@ -68,7 +68,7 @@ func TestLoadModuleCall(t *testing.T) { { Name: "baz", SourceAddr: addrs.ModuleSourceRemote{ - PackageAddr: addrs.ModulePackage("git::https://example.com/"), + Package: addrs.ModulePackage("git::https://example.com/"), }, SourceAddrRaw: "git::https://example.com/", SourceSet: true, diff --git a/internal/configs/provider_validation.go b/internal/configs/provider_validation.go index a6feb4279eaf..facd92520ce2 100644 --- a/internal/configs/provider_validation.go +++ b/internal/configs/provider_validation.go @@ -106,7 +106,7 @@ func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConf ), Subject: &req.DeclRange, }) - } else if req.Type.IsDefault() { + } else if addrs.IsDefaultProvider(req.Type) { // Now check for possible implied duplicates, where a provider // block uses a default namespaced provider, but that provider // was required via a different name. @@ -345,7 +345,7 @@ func validateProviderConfigs(parentCall *ModuleCall, cfg *Config, noProviderConf if !(localName || configAlias || emptyConfig) { // we still allow default configs, so switch to a warning if the incoming provider is a default - if providerAddr.Provider.IsDefault() { + if addrs.IsDefaultProvider(providerAddr.Provider) { diags = append(diags, &hcl.Diagnostic{ Severity: hcl.DiagWarning, Summary: "Reference to undefined provider", diff --git a/internal/getproviders/didyoumean.go b/internal/getproviders/didyoumean.go index d888ccc08a4b..e31ba20e4195 100644 --- a/internal/getproviders/didyoumean.go +++ b/internal/getproviders/didyoumean.go @@ -40,7 +40,7 @@ import ( // renaming suggestion even if one would've been available for a completed // request. func MissingProviderSuggestion(ctx context.Context, addr addrs.Provider, source Source, reqs Requirements) addrs.Provider { - if !addr.IsDefault() { + if !addrs.IsDefaultProvider(addr) { return addr } diff --git a/internal/initwd/module_install.go b/internal/initwd/module_install.go index e9ee4953183c..0bc971860a1b 100644 --- a/internal/initwd/module_install.go +++ b/internal/initwd/module_install.go @@ -306,7 +306,7 @@ func (i *ModuleInstaller) installLocalModule(req *earlyconfig.ModuleRequest, key func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyconfig.ModuleRequest, key string, instPath string, addr addrs.ModuleSourceRegistry, manifest modsdir.Manifest, hooks ModuleInstallHooks, fetcher *getmodules.PackageFetcher) (*tfconfig.Module, *version.Version, tfdiags.Diagnostics) { var diags tfdiags.Diagnostics - hostname := addr.PackageAddr.Host + hostname := addr.Package.Host reg := i.reg var resp *response.ModuleVersions var exists bool @@ -314,7 +314,7 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc // A registry entry isn't _really_ a module package, but we'll pretend it's // one for the sake of this reporting by just trimming off any source // directory. - packageAddr := addr.PackageAddr + packageAddr := addr.Package // Our registry client is still using the legacy model of addresses, so // we'll shim it here for now. @@ -469,9 +469,9 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc dlAddr := i.registryPackageSources[moduleAddr] - log.Printf("[TRACE] ModuleInstaller: %s %s %s is available at %q", key, packageAddr, latestMatch, dlAddr.PackageAddr) + log.Printf("[TRACE] ModuleInstaller: %s %s %s is available at %q", key, packageAddr, latestMatch, dlAddr.Package) - err := fetcher.FetchPackage(ctx, instPath, dlAddr.PackageAddr.String()) + err := fetcher.FetchPackage(ctx, instPath, dlAddr.Package.String()) if errors.Is(err, context.Canceled) { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, @@ -494,7 +494,7 @@ func (i *ModuleInstaller) installRegistryModule(ctx context.Context, req *earlyc return nil, nil, diags } - log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, dlAddr.PackageAddr, instPath) + log.Printf("[TRACE] ModuleInstaller: %s %q was downloaded to %s", key, dlAddr.Package, instPath) // Incorporate any subdir information from the original path into the // address returned by the registry in order to find the final directory @@ -540,7 +540,7 @@ func (i *ModuleInstaller) installGoGetterModule(ctx context.Context, req *earlyc // Report up to the caller that we're about to start downloading. addr := req.SourceAddr.(addrs.ModuleSourceRemote) - packageAddr := addr.PackageAddr + packageAddr := addr.Package hooks.Download(key, packageAddr.String(), nil) if len(req.VersionConstraints) != 0 { @@ -758,7 +758,7 @@ func splitAddrSubdir(addr addrs.ModuleSource) (string, string) { addr.Subdir = "" return addr.String(), subDir case addrs.ModuleSourceRemote: - return addr.PackageAddr.String(), addr.Subdir + return addr.Package.String(), addr.Subdir case nil: panic("splitAddrSubdir on nil addrs.ModuleSource") default: diff --git a/internal/refactoring/move_validate_test.go b/internal/refactoring/move_validate_test.go index aa4ec4f3b800..2a61bdc6c2c9 100644 --- a/internal/refactoring/move_validate_test.go +++ b/internal/refactoring/move_validate_test.go @@ -702,5 +702,5 @@ func makeTestImpliedMoveStmt(t *testing.T, moduleStr, fromStr, toStr string) Mov } var fakeExternalModuleSource = addrs.ModuleSourceRemote{ - PackageAddr: addrs.ModulePackage("fake-external:///"), + Package: addrs.ModulePackage("fake-external:///"), } diff --git a/internal/registry/regsrc/module.go b/internal/registry/regsrc/module.go index 9f9999d3474b..3ffa002bba07 100644 --- a/internal/registry/regsrc/module.go +++ b/internal/registry/regsrc/module.go @@ -105,7 +105,7 @@ func NewModule(host, namespace, name, provider, submodule string) (*Module, erro // use addrs.ModuleSourceRegistry instead, and then package regsrc can be // removed altogether. func ModuleFromModuleSourceAddr(addr addrs.ModuleSourceRegistry) *Module { - ret := ModuleFromRegistryPackageAddr(addr.PackageAddr) + ret := ModuleFromRegistryPackageAddr(addr.Package) ret.RawSubmodule = addr.Subdir return ret }