From 3875ea36560d0383541f48db3f4e863e0e2f7c70 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Tue, 11 Jun 2024 16:33:28 -0400 Subject: [PATCH] add -lock-file flag to providers mirror command The providers mirror command was updated to inspect the lock file, however that was not part of the original intent for the command, and it's possible that the command needs to be run without a lock file. Since we have been honoring the lock file for the past few releases, let's keep that consistent and allow disabling the file with `-lock-file=false`. --- .../command/e2etest/providers_mirror_test.go | 143 ++++++++++-------- .../terraform-providers-mirror.tf | 4 +- internal/command/providers_mirror.go | 9 +- 3 files changed, 90 insertions(+), 66 deletions(-) diff --git a/internal/command/e2etest/providers_mirror_test.go b/internal/command/e2etest/providers_mirror_test.go index ed331718a6..12552882c9 100644 --- a/internal/command/e2etest/providers_mirror_test.go +++ b/internal/command/e2etest/providers_mirror_test.go @@ -19,78 +19,97 @@ import ( // interacts directly with Terraform Registry and the full details of that are // tricky to mock. Such a mock is _possible_, but we're using e2etest as a // compromise for now to keep these tests relatively simple. - func TestTerraformProvidersMirror(t *testing.T) { - testTerraformProvidersMirror(t, "terraform-providers-mirror", "") -} - -func TestTerraformProvidersMirrorWithLockFile(t *testing.T) { - testTerraformProvidersMirror(t, "terraform-providers-mirror-with-lock-file", "") -} - -func TestTerraformProvidersMirrorWithBrokenLockFile(t *testing.T) { - testTerraformProvidersMirror(t, "terraform-providers-mirror-with-broken-lock-file", "Inconsistent dependency lock file") -} - -func testTerraformProvidersMirror(t *testing.T, fixture string, errMsg string) { // This test reaches out to releases.hashicorp.com to download the // template and null providers, so it can only run if network access is // allowed. skipIfCannotAccessNetwork(t) - outputDir := t.TempDir() - t.Logf("creating mirror directory in %s", outputDir) + for _, test := range []struct { + name string + args []string + err string + }{ + { + name: "terraform-providers-mirror", + args: []string{"-platform=linux_amd64", "-platform=windows_386"}, + }, + { + name: "terraform-providers-mirror-with-lock-file", + args: []string{"-platform=linux_amd64", "-platform=windows_386"}, + }, + { + // should ignore lock file + name: "terraform-providers-mirror-with-broken-lock-file", + args: []string{"-platform=linux_amd64", "-platform=windows_386", "-lock-file=false"}, + }, + { + name: "terraform-providers-mirror-with-broken-lock-file", + args: []string{"-platform=linux_amd64", "-platform=windows_386", "-lock-file=true"}, + err: "Inconsistent dependency lock file", + }, + } { + t.Run(test.name, func(t *testing.T) { + outputDir := t.TempDir() + t.Logf("creating mirror directory in %s", outputDir) - fixturePath := filepath.Join("testdata", fixture) - tf := e2e.NewBinary(t, terraformBin, fixturePath) + fixturePath := filepath.Join("testdata", test.name) + tf := e2e.NewBinary(t, terraformBin, fixturePath) - stdout, stderr, err := tf.Run("providers", "mirror", "-platform=linux_amd64", "-platform=windows_386", outputDir) - if errMsg != "" { - if !strings.Contains(stderr, errMsg) { - t.Fatalf("expected error %q, got %q\n", errMsg, stderr) - } - return - } + args := []string{"providers", "mirror"} + args = append(args, test.args...) + args = append(args, outputDir) - if err != nil { - t.Fatalf("unexpected error: %s\nstdout:\n%s\nstderr:\n%s", err, stdout, stderr) - } + stdout, stderr, err := tf.Run(args...) + if test.err != "" { + if !strings.Contains(stderr, test.err) { + t.Fatalf("expected error %q, got %q\n", test.err, stderr) + } + return + } - // The test fixture includes exact version constraints for the two - // providers it depends on so that the following should remain stable. - // In the (unlikely) event that these particular versions of these - // providers are removed from the registry, this test will start to fail. - want := []string{ - "registry.terraform.io/hashicorp/null/2.1.0.json", - "registry.terraform.io/hashicorp/null/index.json", - "registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip", - "registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_windows_386.zip", - "registry.terraform.io/hashicorp/template/2.1.1.json", - "registry.terraform.io/hashicorp/template/index.json", - "registry.terraform.io/hashicorp/template/terraform-provider-template_2.1.1_linux_amd64.zip", - "registry.terraform.io/hashicorp/template/terraform-provider-template_2.1.1_windows_386.zip", - } - var got []string - walkErr := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { - if err != nil { - return err - } - if info.IsDir() { - return nil // we only care about leaf files for this test - } - relPath, err := filepath.Rel(outputDir, path) - if err != nil { - return err - } - got = append(got, filepath.ToSlash(relPath)) - return nil - }) - if walkErr != nil { - t.Fatal(walkErr) - } - sort.Strings(got) + if err != nil { + t.Fatalf("unexpected error: %s\nstdout:\n%s\nstderr:\n%s", err, stdout, stderr) + } + + // The test fixture includes exact version constraints for the two + // providers it depends on so that the following should remain stable. + // In the (unlikely) event that these particular versions of these + // providers are removed from the registry, this test will start to fail. + want := []string{ + "registry.terraform.io/hashicorp/null/2.1.0.json", + "registry.terraform.io/hashicorp/null/index.json", + "registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_linux_amd64.zip", + "registry.terraform.io/hashicorp/null/terraform-provider-null_2.1.0_windows_386.zip", + "registry.terraform.io/hashicorp/template/2.1.1.json", + "registry.terraform.io/hashicorp/template/index.json", + "registry.terraform.io/hashicorp/template/terraform-provider-template_2.1.1_linux_amd64.zip", + "registry.terraform.io/hashicorp/template/terraform-provider-template_2.1.1_windows_386.zip", + } + var got []string + walkErr := filepath.Walk(outputDir, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil // we only care about leaf files for this test + } + relPath, err := filepath.Rel(outputDir, path) + if err != nil { + return err + } + got = append(got, filepath.ToSlash(relPath)) + return nil + }) + if walkErr != nil { + t.Fatal(walkErr) + } + sort.Strings(got) + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("unexpected files in result\n%s", diff) + } - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("unexpected files in result\n%s", diff) + }) } } diff --git a/internal/command/e2etest/testdata/terraform-providers-mirror-with-broken-lock-file/terraform-providers-mirror.tf b/internal/command/e2etest/testdata/terraform-providers-mirror-with-broken-lock-file/terraform-providers-mirror.tf index 1598a27835..4b31e03012 100644 --- a/internal/command/e2etest/testdata/terraform-providers-mirror-with-broken-lock-file/terraform-providers-mirror.tf +++ b/internal/command/e2etest/testdata/terraform-providers-mirror-with-broken-lock-file/terraform-providers-mirror.tf @@ -1,7 +1,7 @@ terraform { required_providers { - template = { source = "hashicorp/template" } - null = { source = "hashicorp/null" } + template = { version = "2.1.1" } + null = { source = "hashicorp/null", version = "2.1.0" } terraform = { source = "terraform.io/builtin/terraform" } } } diff --git a/internal/command/providers_mirror.go b/internal/command/providers_mirror.go index 87143a8851..ec2904eda1 100644 --- a/internal/command/providers_mirror.go +++ b/internal/command/providers_mirror.go @@ -34,8 +34,13 @@ func (c *ProvidersMirrorCommand) Synopsis() string { func (c *ProvidersMirrorCommand) Run(args []string) int { args = c.Meta.process(args) cmdFlags := c.Meta.defaultFlagSet("providers mirror") + var optPlatforms arguments.FlagStringSlice cmdFlags.Var(&optPlatforms, "platform", "target platform") + + var optLockFile bool + cmdFlags.BoolVar(&optLockFile, "lock-file", true, "use lock file") + cmdFlags.Usage = func() { c.Ui.Error(c.Help()) } if err := cmdFlags.Parse(args); err != nil { c.Ui.Error(fmt.Sprintf("Error parsing command-line flags: %s\n", err.Error())) @@ -89,7 +94,7 @@ func (c *ProvidersMirrorCommand) Run(args []string) int { diags = diags.Append(lockedDepsDiags) // If lock file is present, validate it against configuration - if !lockedDeps.Empty() { + if !lockedDeps.Empty() && optLockFile { if errs := config.VerifyDependencySelections(lockedDeps); len(errs) > 0 { diags = diags.Append(tfdiags.Sourceless( tfdiags.Error, @@ -161,7 +166,7 @@ func (c *ProvidersMirrorCommand) Run(args []string) int { continue } selected := candidates.Newest() - if !lockedDeps.Empty() { + if !lockedDeps.Empty() && optLockFile { selected = lockedDeps.Provider(provider).Version() c.Ui.Output(fmt.Sprintf(" - Selected v%s to match dependency lock file", selected.String())) } else if len(constraintsStr) > 0 {