Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check lock file diagnostics in mirror command #35322

Merged
merged 3 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
135 changes: 83 additions & 52 deletions internal/command/e2etest/providers_mirror_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"testing"

"github.com/google/go-cmp/cmp"
Expand All @@ -18,67 +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 testTerraformProvidersMirror(t *testing.T, fixture 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 err != nil {
t.Fatalf("unexpected error: %s\nstdout:\n%s\nstderr:\n%s", err, stdout, stderr)
}
args := []string{"providers", "mirror"}
args = append(args, test.args...)
args = append(args, outputDir)

// 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)
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
}

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)
})
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
terraform {
required_providers {
template = { version = "2.1.1" }
null = { source = "hashicorp/null", version = "2.1.0" }
terraform = { source = "terraform.io/builtin/terraform" }
}
}
21 changes: 13 additions & 8 deletions internal/command/providers_mirror.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down Expand Up @@ -88,14 +93,8 @@ func (c *ProvidersMirrorCommand) Run(args []string) int {
lockedDeps, lockedDepsDiags := c.Meta.lockedDependencies()
diags = diags.Append(lockedDepsDiags)

// If we have any error diagnostics already then we won't proceed further.
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

// 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,
Expand All @@ -105,6 +104,12 @@ func (c *ProvidersMirrorCommand) Run(args []string) int {
}
}

// If we have any error diagnostics already then we won't proceed further.
if diags.HasErrors() {
c.showDiagnostics(diags)
return 1
}

// Unlike other commands, this command always consults the origin registry
// for every provider so that it can be used to update a local mirror
// directory without needing to first disable that local mirror
Expand Down Expand Up @@ -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 {
Expand Down
Loading