From a549c3b124216d82da54148d802bbb2e8d8af553 Mon Sep 17 00:00:00 2001 From: Levko Burburas <62853952+levkohimins@users.noreply.github.com> Date: Fri, 17 May 2024 16:03:58 +0300 Subject: [PATCH] fix: terraform source url handling (#3142) --- terraform/source.go | 32 ++++++++++++++++++++++++++++++-- terraform/source_test.go | 4 +++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/terraform/source.go b/terraform/source.go index 41cdab368..0e495886e 100644 --- a/terraform/source.go +++ b/terraform/source.go @@ -191,8 +191,10 @@ func NewSource(source string, downloadDir string, workingDir string, logger *log // Convert the given source into a URL struct. This method should be able to handle all source URLs that the terraform // init command can handle, parsing local file paths, Git paths, and HTTP URLs correctly. func ToSourceUrl(source string, workingDir string) (*url.URL, error) { - // we need to remove the http(s) scheme to allow `getter.Detect` to add the source type - source = httpSchemeRegexp.ReplaceAllString(source, "") + source, err := normalizeSourceURL(source, workingDir) + if err != nil { + return nil, err + } // The go-getter library is what Terraform's init command uses to download source URLs. Use that library to // parse the URL. @@ -204,6 +206,32 @@ func ToSourceUrl(source string, workingDir string) (*url.URL, error) { return parseSourceUrl(rawSourceUrlWithGetter) } +// We have to remove the http(s) scheme from the source URL to allow `getter.Detect` to add the source type, but only if the `getter` has a detector for that host. +func normalizeSourceURL(source string, workingDir string) (string, error) { + newSource := httpSchemeRegexp.ReplaceAllString(source, "") + + // We can't use `the getter.Detectors` global variable because we need to exclude `getter.FileDetector` from checking since it is not a URL detector. + detectors := []getter.Detector{ + new(getter.GitHubDetector), + new(getter.GitLabDetector), + new(getter.GitDetector), + new(getter.BitBucketDetector), + new(getter.S3Detector), + new(getter.GCSDetector), + } + + for _, detector := range detectors { + _, ok, err := detector.Detect(newSource, workingDir) + if err != nil { + return source, errors.WithStackTrace(err) + } + if ok { + return newSource, nil + } + } + return source, nil +} + // Parse the given source URL into a URL struct. This method can handle source URLs that include go-getter's "forced // getter" prefixes, such as git::. func parseSourceUrl(source string) (*url.URL, error) { diff --git a/terraform/source_test.go b/terraform/source_test.go index 663e3283a..809472d0b 100644 --- a/terraform/source_test.go +++ b/terraform/source_test.go @@ -3,6 +3,7 @@ package terraform import ( "fmt" "net/url" + "os" "testing" "github.com/stretchr/testify/assert" @@ -78,6 +79,7 @@ func TestToSourceUrl(t *testing.T) { {"https://www.googleapis.com/storage/v1/modules/foomodule.zip", "gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zip"}, {"https://www.googleapis.com/storage/v1/modules/foomodule.zip", "gcs::https://www.googleapis.com/storage/v1/modules/foomodule.zip"}, {"git::https://name@dev.azure.com/name/project-name/_git/repo-name", "git::https://name@dev.azure.com/name/project-name/_git/repo-name"}, + {"https://repositry.rnd.net/artifactory/generic-production-iac/tf-auto-azr-iam.2.6.0.zip", "https://repositry.rnd.net/artifactory/generic-production-iac/tf-auto-azr-iam.2.6.0.zip"}, } for i, testCase := range testCases { @@ -86,7 +88,7 @@ func TestToSourceUrl(t *testing.T) { t.Run(fmt.Sprintf("testCase-%d", i), func(t *testing.T) { t.Parallel() - actualSourceURL, err := ToSourceUrl(testCase.sourceURL, "") + actualSourceURL, err := ToSourceUrl(testCase.sourceURL, os.TempDir()) require.NoError(t, err) assert.Equal(t, testCase.expectedSourceURL, actualSourceURL.String()) })