From 5789a5311ae20a272af36ebf1a115d147f651c69 Mon Sep 17 00:00:00 2001 From: "phillip.nielsen" Date: Wed, 28 Jul 2021 15:41:39 -0500 Subject: [PATCH 1/2] Add `providers lock` Command Adds feature requested in #201. This allows programatic generation of `.terraform.lock.hcl` files via this library --- .../internal/e2etest/providers_lock_test.go | 26 +++++++ tfexec/options.go | 36 +++++++++ tfexec/providers_lock.go | 76 +++++++++++++++++++ tfexec/providers_lock_test.go | 42 ++++++++++ 4 files changed, 180 insertions(+) create mode 100644 tfexec/internal/e2etest/providers_lock_test.go create mode 100644 tfexec/providers_lock.go create mode 100644 tfexec/providers_lock_test.go diff --git a/tfexec/internal/e2etest/providers_lock_test.go b/tfexec/internal/e2etest/providers_lock_test.go new file mode 100644 index 00000000..b6c9a83e --- /dev/null +++ b/tfexec/internal/e2etest/providers_lock_test.go @@ -0,0 +1,26 @@ +package e2etest + +import ( + "context" + "testing" + + "github.com/hashicorp/go-version" + + "github.com/hashicorp/terraform-exec/tfexec" + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestProvidersLock(t *testing.T) { + runTestVersions(t, []string{testutil.Latest014, testutil.Latest015, testutil.Latest_v1}, "basic", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + err := tf.Init(context.Background()) + if err != nil { + t.Fatalf("error running Init in test directory: %s", err) + } + + err = tf.ProvidersLock(context.Background()) + if err != nil { + t.Fatalf("error running provider lock: %s", err) + } + }) + +} diff --git a/tfexec/options.go b/tfexec/options.go index d7890107..bc98ab72 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -117,6 +117,15 @@ func DryRun(dryRun bool) *DryRunOption { return &DryRunOption{dryRun} } +type FSMirrorOption struct { + fsMirror string +} + +// FSMirror Represents the -fs-mirror option +func FSMirror(fsMirror string) *FSMirrorOption { + return &FSMirrorOption{fsMirror} +} + type ForceOption struct { force bool } @@ -178,6 +187,15 @@ func LockTimeout(lockTimeout string) *LockTimeoutOption { return &LockTimeoutOption{lockTimeout} } +type NetMirrorOption struct { + netMirror string +} + +// NetMirror Represents the -fs-mirror option +func NetMirror(netMirror string) *NetMirrorOption { + return &NetMirrorOption{netMirror} +} + type OutOption struct { path string } @@ -194,6 +212,15 @@ func Parallelism(n int) *ParallelismOption { return &ParallelismOption{n} } +type PlatformOption struct { + platform string +} + +// Platform represents the -platform flag which is an os_arch string +func Platform(platform string) *PlatformOption { + return &PlatformOption{platform} +} + type PluginDirOption struct { pluginDir string } @@ -202,6 +229,15 @@ func PluginDir(pluginDir string) *PluginDirOption { return &PluginDirOption{pluginDir} } +type ProviderOption struct { + provider string +} + +// Provider Represents the provider positional argument +func Provider(providers string) *ProviderOption { + return &ProviderOption{providers} +} + type ReattachInfo map[string]ReattachConfig // ReattachConfig holds the information Terraform needs to be able to attach diff --git a/tfexec/providers_lock.go b/tfexec/providers_lock.go new file mode 100644 index 00000000..88289ced --- /dev/null +++ b/tfexec/providers_lock.go @@ -0,0 +1,76 @@ +package tfexec + +import ( + "context" + "os/exec" +) + +type providersLockConfig struct { + fsMirror string + netMirror string + platforms []string + providers []string +} + +var defaultProvidersLockOptions = providersLockConfig{} + +type ProvidersLockOption interface { + configureProvidersLock(*providersLockConfig) +} + +func (opt *FSMirrorOption) configureProvidersLock(conf *providersLockConfig) { + conf.fsMirror = opt.fsMirror +} + +func (opt *NetMirrorOption) configureProvidersLock(conf *providersLockConfig) { + conf.netMirror = opt.netMirror +} + +func (opt *PlatformOption) configureProvidersLock(conf *providersLockConfig) { + conf.platforms = append(conf.platforms, opt.platform) +} + +func (opt *ProviderOption) configureProvidersLock(conf *providersLockConfig) { + conf.providers = append(conf.providers, opt.provider) +} + +// ProvidersLock represents the terraform providers lock +func (tf *Terraform) ProvidersLock(ctx context.Context, opts ...ProvidersLockOption) error { + lockCmd := tf.providersLockCmd(ctx, opts...) + + err := tf.runTerraformCmd(ctx, lockCmd) + if err != nil { + return err + } + + return err +} + +func (tf *Terraform) providersLockCmd(ctx context.Context, opts ...ProvidersLockOption) *exec.Cmd { + c := defaultProvidersLockOptions + + for _, o := range opts { + o.configureProvidersLock(&c) + } + args := []string{"providers", "lock"} + + // string options, only pass if set + if c.fsMirror != "" { + args = append(args, "-fs-mirror="+c.fsMirror) + } + + if c.netMirror != "" { + args = append(args, "-net-mirror="+c.netMirror) + } + + for _, p := range c.platforms { + args = append(args, "-platform="+p) + } + + // positional providers argument + for _, p := range c.providers { + args = append(args, p) + } + + return tf.buildTerraformCmd(ctx, nil, args...) +} diff --git a/tfexec/providers_lock_test.go b/tfexec/providers_lock_test.go new file mode 100644 index 00000000..9d5b8dbc --- /dev/null +++ b/tfexec/providers_lock_test.go @@ -0,0 +1,42 @@ +package tfexec + +import ( + "context" + "testing" + + "github.com/hashicorp/terraform-exec/tfexec/internal/testutil" +) + +func TestProvidersLockCmd(t *testing.T) { + td := testTempDir(t) + + tf, err := NewTerraform(td, tfVersion(t, testutil.Latest012)) + 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) { + lockCmd := tf.providersLockCmd(context.Background()) + + assertCmd(t, []string{ + "providers", + "lock", + }, nil, lockCmd) + }) + + t.Run("override all defaults", func(t *testing.T) { + lockCmd := tf.providersLockCmd(context.Background(), FSMirror("test"), NetMirror("test"), Platform("linux_amd64"), Provider("workingdir")) + + assertCmd(t, []string{ + "providers", + "lock", + "-fs-mirror=test", + "-net-mirror=test", + "-platform=linux_amd64", + "workingdir", + }, nil, lockCmd) + }) +} From 381a404bcc1f0420fe3d214b93d1a5c009da6e98 Mon Sep 17 00:00:00 2001 From: "phillip.nielsen" Date: Tue, 3 Aug 2021 15:05:55 -0500 Subject: [PATCH 2/2] Add Min Version Check To `providers lock` This command was added in 0.14 so it should helpfully error out if used with a version before that. Also cleaned up some godoc comments per PR feedback. --- tfexec/options.go | 6 +++--- tfexec/providers_lock.go | 10 ++++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/tfexec/options.go b/tfexec/options.go index bc98ab72..74fd96a0 100644 --- a/tfexec/options.go +++ b/tfexec/options.go @@ -121,7 +121,7 @@ type FSMirrorOption struct { fsMirror string } -// FSMirror Represents the -fs-mirror option +// FSMirror represents the -fs-mirror option (path to filesystem mirror directory) func FSMirror(fsMirror string) *FSMirrorOption { return &FSMirrorOption{fsMirror} } @@ -191,7 +191,7 @@ type NetMirrorOption struct { netMirror string } -// NetMirror Represents the -fs-mirror option +// NetMirror represents the -net-mirror option (base URL of a network mirror) func NetMirror(netMirror string) *NetMirrorOption { return &NetMirrorOption{netMirror} } @@ -233,7 +233,7 @@ type ProviderOption struct { provider string } -// Provider Represents the provider positional argument +// Provider represents the positional argument (provider source address) func Provider(providers string) *ProviderOption { return &ProviderOption{providers} } diff --git a/tfexec/providers_lock.go b/tfexec/providers_lock.go index 88289ced..b3a20216 100644 --- a/tfexec/providers_lock.go +++ b/tfexec/providers_lock.go @@ -2,6 +2,7 @@ package tfexec import ( "context" + "fmt" "os/exec" ) @@ -34,11 +35,16 @@ func (opt *ProviderOption) configureProvidersLock(conf *providersLockConfig) { conf.providers = append(conf.providers, opt.provider) } -// ProvidersLock represents the terraform providers lock +// ProvidersLock represents the `terraform providers lock` command func (tf *Terraform) ProvidersLock(ctx context.Context, opts ...ProvidersLockOption) error { + err := tf.compatible(ctx, tf0_14_0, nil) + if err != nil { + return fmt.Errorf("terraform providers lock was added in 0.14.0: %w", err) + } + lockCmd := tf.providersLockCmd(ctx, opts...) - err := tf.runTerraformCmd(ctx, lockCmd) + err = tf.runTerraformCmd(ctx, lockCmd) if err != nil { return err }