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

Introduce tfexec.ErrStateLocked to represent locked state error #221

Merged
merged 7 commits into from
Oct 4, 2021
Merged
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
48 changes: 48 additions & 0 deletions tfexec/exit_errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os/exec"
"regexp"
"strings"
"text/template"
)

// this file contains errors parsed from stderr
Expand All @@ -30,6 +31,9 @@ var (
tfVersionMismatchErrRegexp = regexp.MustCompile(`Error: The currently running version of Terraform doesn't meet the|Error: Unsupported Terraform Core version`)
tfVersionMismatchConstraintRegexp = regexp.MustCompile(`required_version = "(.+)"|Required version: (.+)\b`)
configInvalidErrRegexp = regexp.MustCompile(`There are some problems with the configuration, described below.`)

stateLockErrRegexp = regexp.MustCompile(`Error acquiring the state lock`)
stateLockInfoRegexp = regexp.MustCompile(`Lock Info:\n\s*ID:\s*([^\n]+)\n\s*Path:\s*([^\n]+)\n\s*Operation:\s*([^\n]+)\n\s*Who:\s*([^\n]+)\n\s*Version:\s*([^\n]+)\n\s*Created:\s*([^\n]+)\n`)
)

func (tf *Terraform) wrapExitError(ctx context.Context, err error, stderr string) error {
Expand Down Expand Up @@ -128,6 +132,20 @@ func (tf *Terraform) wrapExitError(ctx context.Context, err error, stderr string
}
case configInvalidErrRegexp.MatchString(stderr):
return &ErrConfigInvalid{stderr: stderr}
case stateLockErrRegexp.MatchString(stderr):
submatches := stateLockInfoRegexp.FindStringSubmatch(stderr)
if len(submatches) == 7 {
return &ErrStateLocked{
unwrapper: unwrapper{exitErr, ctxErr},

ID: submatches[1],
Path: submatches[2],
Operation: submatches[3],
Who: submatches[4],
Version: submatches[5],
Created: submatches[6],
}
}
}

return fmt.Errorf("%w\n%s", &unwrapper{exitErr, ctxErr}, stderr)
Expand Down Expand Up @@ -257,3 +275,33 @@ func (e *ErrTFVersionMismatch) Error() string {
return fmt.Sprintf("terraform %s not supported by configuration%s",
version, requirement)
}

// ErrStateLocked is returned when the state lock is already held by another process.
type ErrStateLocked struct {
unwrapper

ID string
Path string
Operation string
Who string
Version string
Created string
}

func (e *ErrStateLocked) Error() string {
tmpl := `Lock Info:
ID: {{.ID}}
Path: {{.Path}}
Operation: {{.Operation}}
Who: {{.Who}}
Version: {{.Version}}
Created: {{.Created}}
`

t := template.Must(template.New("LockInfo").Parse(tmpl))
var out strings.Builder
if err := t.Execute(&out, e); err != nil {
return "error acquiring the state lock"
}
return fmt.Sprintf("error acquiring the state lock: %v", out.String())
}
19 changes: 19 additions & 0 deletions tfexec/internal/e2etest/errors_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,25 @@ func TestTFVersionMismatch(t *testing.T) {
})
}

func TestLockedState(t *testing.T) {
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("err during init: %s", err)
}

err = tf.Apply(context.Background())
if err == nil {
t.Fatal("expected error, but didn't find one")
}

var stateLockedErr *tfexec.ErrStateLocked
if !errors.As(err, &stateLockedErr) {
t.Fatalf("expected ErrTFVersionMismatch, got %T, %s", err, err)
}
})
}

func TestContext_alreadyPastDeadline(t *testing.T) {
runTest(t, "", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) {
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(-1*time.Second))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
terraform {
backend "inmem" {
lock_id = "2b6a6738-5dd5-50d6-c0ae-f6352977666b"
}
}