add tests for ForceUnlock (#330)
* add tests for ForceUnlock
* add compatibility code for dir arg
* generate lock id error for all backend types
diff --git a/tfexec/exit_errors.go b/tfexec/exit_errors.go
index ea25b2a..9fc152d 100644
--- a/tfexec/exit_errors.go
+++ b/tfexec/exit_errors.go
@@ -46,6 +46,7 @@
statePlanReadErrRegexp = regexp.MustCompile(
`Terraform couldn't read the given file as a state or plan file.|` +
`Error: Failed to read the given file as a state or plan file`)
+ lockIdInvalidErrRegexp = regexp.MustCompile(`Failed to unlock state: `)
)
func (tf *Terraform) wrapExitError(ctx context.Context, err error, stderr string) error {
@@ -160,6 +161,8 @@
}
case statePlanReadErrRegexp.MatchString(stderr):
return &ErrStatePlanRead{stderr: stderr}
+ case lockIdInvalidErrRegexp.MatchString(stderr):
+ return &ErrLockIdInvalid{stderr: stderr}
}
return fmt.Errorf("%w\n%s", &unwrapper{exitErr, ctxErr}, stderr)
@@ -256,6 +259,16 @@
return e.stderr
}
+type ErrLockIdInvalid struct {
+ unwrapper
+
+ stderr string
+}
+
+func (e *ErrLockIdInvalid) Error() string {
+ return e.stderr
+}
+
// ErrCLIUsage is returned when the combination of flags or arguments is incorrect.
//
// CLI indicates usage errors in three different ways: either
diff --git a/tfexec/force_unlock.go b/tfexec/force_unlock.go
index e501baf..de95f54 100644
--- a/tfexec/force_unlock.go
+++ b/tfexec/force_unlock.go
@@ -2,6 +2,7 @@
import (
"context"
+ "fmt"
"os/exec"
)
@@ -21,7 +22,10 @@
// ForceUnlock represents the `terraform force-unlock` command
func (tf *Terraform) ForceUnlock(ctx context.Context, lockID string, opts ...ForceUnlockOption) error {
- unlockCmd := tf.forceUnlockCmd(ctx, lockID, opts...)
+ unlockCmd, err := tf.forceUnlockCmd(ctx, lockID, opts...)
+ if err != nil {
+ return err
+ }
if err := tf.runTerraformCmd(ctx, unlockCmd); err != nil {
return err
@@ -30,7 +34,7 @@
return nil
}
-func (tf *Terraform) forceUnlockCmd(ctx context.Context, lockID string, opts ...ForceUnlockOption) *exec.Cmd {
+func (tf *Terraform) forceUnlockCmd(ctx context.Context, lockID string, opts ...ForceUnlockOption) (*exec.Cmd, error) {
c := defaultForceUnlockOptions
for _, o := range opts {
@@ -43,8 +47,12 @@
// optional positional arguments
if c.dir != "" {
+ err := tf.compatible(ctx, nil, tf0_15_0)
+ if err != nil {
+ return nil, fmt.Errorf("[DIR] option was removed in Terraform v0.15.0")
+ }
args = append(args, c.dir)
}
- return tf.buildTerraformCmd(ctx, nil, args...)
+ return tf.buildTerraformCmd(ctx, nil, args...), nil
}
diff --git a/tfexec/force_unlock_test.go b/tfexec/force_unlock_test.go
new file mode 100644
index 0000000..b817979
--- /dev/null
+++ b/tfexec/force_unlock_test.go
@@ -0,0 +1,63 @@
+package tfexec
+
+import (
+ "context"
+ "testing"
+
+ "github.com/hashicorp/terraform-exec/tfexec/internal/testutil"
+)
+
+func TestForceUnlockCmd(t *testing.T) {
+ td := t.TempDir()
+
+ tf, err := NewTerraform(td, tfVersion(t, testutil.Latest_v1_1))
+ 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) {
+ forceUnlockCmd, err := tf.forceUnlockCmd(context.Background(), "12345")
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ assertCmd(t, []string{
+ "force-unlock",
+ "-no-color",
+ "-force",
+ "12345",
+ }, nil, forceUnlockCmd)
+ })
+}
+
+// The optional final positional [DIR] argument is available
+// until v0.15.0.
+func TestForceUnlockCmd_pre015(t *testing.T) {
+ td := t.TempDir()
+
+ tf, err := NewTerraform(td, tfVersion(t, testutil.Latest014))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ // empty env, to avoid environ mismatch in testing
+ tf.SetEnv(map[string]string{})
+
+ t.Run("override all defaults", func(t *testing.T) {
+ forceUnlockCmd, err := tf.forceUnlockCmd(context.Background(), "12345", Dir("mydir"))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ assertCmd(t, []string{
+ "force-unlock",
+ "-no-color",
+ "-force",
+ "12345",
+ "mydir",
+ }, nil, forceUnlockCmd)
+ })
+}
diff --git a/tfexec/internal/e2etest/force_unlock_test.go b/tfexec/internal/e2etest/force_unlock_test.go
index 4665727..c5d626a 100644
--- a/tfexec/internal/e2etest/force_unlock_test.go
+++ b/tfexec/internal/e2etest/force_unlock_test.go
@@ -2,6 +2,7 @@
import (
"context"
+ "errors"
"testing"
"github.com/hashicorp/go-version"
@@ -40,4 +41,19 @@
t.Fatalf("error running ForceUnlock: %v", err)
}
})
+ runTest(t, "inmem_backend_locked", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
+ err := tf.Init(context.Background())
+ if err != nil {
+ t.Fatalf("error running Init: %v", err)
+ }
+
+ err = tf.ForceUnlock(context.Background(), "badlockid")
+ if err == nil {
+ t.Fatalf("expected error when running ForceUnlock with invalid lock id")
+ }
+ var foErr *tfexec.ErrLockIdInvalid
+ if !errors.As(err, &foErr) {
+ t.Fatalf("expected ErrLockIdInvalid, %T returned: %s", err, err)
+ }
+ })
}