Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion tfexec/internal/e2etest/providers_lock_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func TestProvidersLock(t *testing.T) {

err = tf.ProvidersLock(context.Background())
if err != nil {
t.Fatalf("error running provider lock: %s", err)
t.Fatalf("error running providers lock: %s", err)
}
})

Expand Down
61 changes: 61 additions & 0 deletions tfexec/internal/e2etest/providers_mirror_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0
package e2etest

import (
"context"
"os"
"path/filepath"
"testing"

"github.com/hashicorp/go-version"

"github.com/hashicorp/terraform-exec/tfexec"
)

var (
providersMirrorMinVersion = version.Must(version.NewVersion("0.13.0"))
providersMirrorLockFileMinVersion = version.Must(version.NewVersion("1.10.0"))
)

func TestProvidersMirror(t *testing.T) {
runTest(t, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
if tfv.LessThan(providersMirrorMinVersion) {
t.Skip("terraform providers mirror was added in Terraform 0.13, so test is not valid")
}
err := tf.Init(context.Background())
if err != nil {
t.Fatalf("error running Init in test directory: %s", err)
}

targetDir := t.TempDir()
err = tf.ProvidersMirror(context.Background(), targetDir)
if err != nil {
t.Fatalf("error running providers mirror: %s", err)
}

expectedMirrorPath := filepath.Join(targetDir, "registry.terraform.io", "hashicorp", "null")
_, err = os.Stat(expectedMirrorPath)
if err != nil {
t.Fatalf("providers mirror not found in %s", expectedMirrorPath)
}
})
}

func TestProvidersMirror_lockFileFalse(t *testing.T) {
runTest(t, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
if tfv.LessThan(providersMirrorLockFileMinVersion) {
t.Skip("terraform providers mirror -lock-file flag was added in Terraform 1.10, so test is not valid")
}
err := tf.Init(context.Background())
if err != nil {
t.Fatalf("error running Init in test directory: %s", err)
}

targetDir := t.TempDir()
err = tf.ProvidersMirror(context.Background(), targetDir, tfexec.LockFile(false))
if err != nil {
t.Fatalf("error running providers mirror: %s", err)
}
})
}
4 changes: 2 additions & 2 deletions tfexec/internal/e2etest/show_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ func TestShow_noInitBasic(t *testing.T) {
// no providers to download, this is unintended behaviour, as
// init is not actually necessary. This is considered a known issue in
// pre-1.2.0 versions.
runTestWithVersions(t, []string{testutil.Latest012, testutil.Latest013, testutil.Latest014, testutil.Latest015, testutil.Latest_v1, testutil.Latest_v1_1}, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
runTestWithVersions(t, []string{testutil.Latest012, testutil.Latest013, testutil.Latest014, testutil.Latest015, testutil.Latest_v1_0, testutil.Latest_v1_1}, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
_, err := tf.Show(context.Background())
if err == nil {
t.Fatalf("expected error, but did not get one")
Expand Down Expand Up @@ -158,7 +158,7 @@ func TestShow_noInitModule(t *testing.T) {
// no providers to download, this is unintended behaviour, as
// init is not actually necessary. This is considered a known issue in
// pre-1.2.0 versions.
runTestWithVersions(t, []string{testutil.Latest012, testutil.Latest013, testutil.Latest014, testutil.Latest015, testutil.Latest_v1, testutil.Latest_v1_1}, "registry_module", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
runTestWithVersions(t, []string{testutil.Latest012, testutil.Latest013, testutil.Latest014, testutil.Latest015, testutil.Latest_v1_0, testutil.Latest_v1_1}, "registry_module", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
_, err := tf.Show(context.Background())
if err == nil {
t.Fatalf("expected error, but did not get one")
Expand Down
15 changes: 9 additions & 6 deletions tfexec/internal/testutil/tfcache.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,13 @@ import (
)

const (
Latest011 = "0.11.15"
Latest012 = "0.12.31"
Latest013 = "0.13.7"
Latest014 = "0.14.11"
Latest015 = "0.15.5"
Latest_v1 = "1.0.11"
Latest011 = "0.11.15"
Latest012 = "0.12.31"
Latest013 = "0.13.7"
Latest014 = "0.14.11"
Latest015 = "0.15.5"

Latest_v1_0 = "1.0.11"
Latest_v1_1 = "1.1.9"
Latest_v1_2 = "1.2.9"
Latest_v1_3 = "1.3.10"
Expand All @@ -37,6 +38,8 @@ const (
Latest_v1_11 = "1.11.4"
Latest_v1_12 = "1.12.2"
Latest_Alpha_v1_14 = "1.14.0-alpha20250903"

Latest_v1 = "1.14.0"
)

const appendUserAgent = "tfexec-testutil"
Expand Down
9 changes: 9 additions & 0 deletions tfexec/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,3 +458,12 @@ type VerifyPluginsOption struct {
func VerifyPlugins(verifyPlugins bool) *VerifyPluginsOption {
return &VerifyPluginsOption{verifyPlugins}
}

// LockFileOption represents the -lock-file flag.
type LockFileOption struct {
useLockFile bool
}

func LockFile(useLockFile bool) *LockFileOption {
return &LockFileOption{useLockFile: useLockFile}
}
85 changes: 85 additions & 0 deletions tfexec/providers_mirror.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfexec

import (
"context"
"fmt"
"os/exec"
)

type providersMirrorConfig struct {
lockFile bool
platforms []string
}

var defaultProvidersMirrorOptions = providersMirrorConfig{
// defaults to true
// See https://github.com/hashicorp/terraform/blob/v1.14.0/internal/command/providers_mirror.go#L42
lockFile: true,
}

type ProvidersMirrorOption interface {
configureProvidersMirror(*providersMirrorConfig)
}

func (opt *LockFileOption) configureProvidersMirror(conf *providersMirrorConfig) {
conf.lockFile = opt.useLockFile
}

func (opt *PlatformOption) configureProvidersMirror(conf *providersMirrorConfig) {
conf.platforms = append(conf.platforms, opt.platform)
}

// ProvidersMirror represents the `terraform providers mirror` command
func (tf *Terraform) ProvidersMirror(ctx context.Context, targetDir string, opts ...ProvidersMirrorOption) error {
err := tf.compatible(ctx, tf0_13_0, nil)
if err != nil {
return fmt.Errorf("terraform providers mirror was added in 0.13.0: %w", err)
}
if targetDir == "" {
return fmt.Errorf("targetDir argument needs to be set")
}

mirrorCmd, err := tf.providersMirrorCmd(ctx, targetDir, opts...)
if err != nil {
return err
}

err = tf.runTerraformCmd(ctx, mirrorCmd)
if err != nil {
return err
}

return err
}

func (tf *Terraform) providersMirrorCmd(ctx context.Context, targetDir string, opts ...ProvidersMirrorOption) (*exec.Cmd, error) {
c := defaultProvidersMirrorOptions

args := []string{"providers", "mirror"}

for _, o := range opts {
o.configureProvidersMirror(&c)
}

for _, p := range c.platforms {
args = append(args, "-platform="+p)
}

// lockFile is true by default, so only pass the flag if the caller has set it
// to false
if !c.lockFile {
err := tf.compatible(ctx, tf1_10_0, nil)
if err != nil {
return nil, fmt.Errorf("lock-file option was introduced in Terraform 1.10.0: %w", err)
}

args = append(args, "-lock-file=false")
}

args = append(args, targetDir)

return tf.buildTerraformCmd(ctx, nil, args...), nil
}
67 changes: 67 additions & 0 deletions tfexec/providers_mirror_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package tfexec

import (
"context"
"testing"

"github.com/hashicorp/terraform-exec/tfexec/internal/testutil"
)

func TestProvidersMirrorCmd(t *testing.T) {
td := t.TempDir()

tf, err := NewTerraform(td, tfVersion(t, testutil.Latest_v1))
if err != nil {
t.Fatal(err)
}

// empty env, to avoid environ mismatch in testing
tf.SetEnv(map[string]string{})

t.Run("defaults", func(t *testing.T) {
mirrorCmd, err := tf.providersMirrorCmd(context.Background(), "path")
if err != nil {
t.Fatal(err)
}

assertCmd(t, []string{
"providers",
"mirror",
"path",
}, nil, mirrorCmd)
})

t.Run("multiple platforms", func(t *testing.T) {
mirrorCmd, err := tf.providersMirrorCmd(context.Background(), "path", Platform("IBM-Z"), Platform("Solaris"), Platform("Commodore64"))
if err != nil {
t.Fatal(err)
}

assertCmd(t, []string{
"providers",
"mirror",
"-platform=IBM-Z",
"-platform=Solaris",
"-platform=Commodore64",
"path",
}, nil, mirrorCmd)
})

t.Run("override all defaults", func(t *testing.T) {
mirrorCmd, err := tf.providersMirrorCmd(context.Background(), "path", LockFile(false), Platform("IBM-Z"))
if err != nil {
t.Fatal(err)
}

assertCmd(t, []string{
"providers",
"mirror",
"-platform=IBM-Z",
"-lock-file=false",
"path",
}, nil, mirrorCmd)
})
}
1 change: 1 addition & 0 deletions tfexec/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ var (
tf1_4_0 = version.Must(version.NewVersion("1.4.0"))
tf1_6_0 = version.Must(version.NewVersion("1.6.0"))
tf1_9_0 = version.Must(version.NewVersion("1.9.0"))
tf1_10_0 = version.Must(version.NewVersion("1.10.0"))
tf1_13_0 = version.Must(version.NewVersion("1.13.0"))
tf1_14_0 = version.Must(version.NewVersion("1.14.0"))
)
Expand Down
Loading