From 065640584ad432ed3f7cc2b0a59d102bc62dda80 Mon Sep 17 00:00:00 2001 From: Paul Tyng Date: Mon, 31 Aug 2020 13:59:50 -0400 Subject: [PATCH] Add well known error for required_version issues --- tfexec/cmd.go | 2 +- tfexec/errors.go | 43 ++++++++++++++++++- tfexec/internal/e2etest/errors_test.go | 29 +++++++++++++ tfexec/internal/e2etest/testdata/tf99/main.tf | 3 ++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 tfexec/internal/e2etest/testdata/tf99/main.tf diff --git a/tfexec/cmd.go b/tfexec/cmd.go index c91be139..69865382 100644 --- a/tfexec/cmd.go +++ b/tfexec/cmd.go @@ -138,7 +138,7 @@ func (tf *Terraform) runTerraformCmd(cmd *exec.Cmd) error { err := cmd.Run() if err != nil { - return parseError(err, errBuf.String()) + return tf.parseError(err, errBuf.String()) } return nil } diff --git a/tfexec/errors.go b/tfexec/errors.go index f4010985..241fe715 100644 --- a/tfexec/errors.go +++ b/tfexec/errors.go @@ -23,14 +23,42 @@ var ( workspaceDoesNotExistRegexp = regexp.MustCompile(`Workspace "(.+)" doesn't exist.`) workspaceAlreadyExistsRegexp = regexp.MustCompile(`Workspace "(.+)" already exists`) + + 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`) ) -func parseError(err error, stderr string) error { +func (tf *Terraform) parseError(err error, stderr string) error { if _, ok := err.(*exec.ExitError); !ok { return err } switch { + case tfVersionMismatchErrRegexp.MatchString(stderr): + constraint := "" + constraints := tfVersionMismatchConstraintRegexp.FindStringSubmatch(stderr) + for i := 1; i < len(constraints); i++ { + constraint = strings.TrimSpace(constraints[i]) + if constraint != "" { + break + } + } + + if constraint == "" { + // hardcode a value here for weird cases (incl. 0.12) + constraint = "unknown" + } + + // only set this if it happened to be cached already + ver := "" + if tf != nil && tf.execVersion != nil { + ver = tf.execVersion.String() + } + + return &ErrTFVersionMismatch{ + Constraint: constraint, + TFVersion: ver, + } case missingVarErrRegexp.MatchString(stderr): name := "" names := missingVarNameRegexp.FindStringSubmatch(stderr) @@ -70,6 +98,19 @@ func (e *ErrNoSuitableBinary) Error() string { return fmt.Sprintf("no suitable terraform binary could be found: %s", e.err.Error()) } +// ErrTFVersionMismatch is returned when the running Terraform version is not compatible with the +// value specified for required_version in the terraform block. +type ErrTFVersionMismatch struct { + TFVersion string + + // Constraint is not returned in the error messaging on 0.12 + Constraint string +} + +func (e *ErrTFVersionMismatch) Error() string { + return "terraform core version not supported by configuration" +} + // ErrVersionMismatch is returned when the detected Terraform version is not compatible with the // command or flags being used in this invocation. type ErrVersionMismatch struct { diff --git a/tfexec/internal/e2etest/errors_test.go b/tfexec/internal/e2etest/errors_test.go index 31804590..cd7b97df 100644 --- a/tfexec/internal/e2etest/errors_test.go +++ b/tfexec/internal/e2etest/errors_test.go @@ -62,3 +62,32 @@ func TestMissingVar(t *testing.T) { } }) } + +func TestTFVersionMismatch(t *testing.T) { + runTest(t, "tf99", func(t *testing.T, tfv *version.Version, tf *tfexec.Terraform) { + // force cache version for error messaging + _, _, err := tf.Version(context.Background(), true) + if err != nil { + t.Fatal(err) + } + + err = tf.Init(context.Background()) + if err == nil { + t.Fatal("expected error, but didn't find one") + } + + var e *tfexec.ErrTFVersionMismatch + if !errors.As(err, &e) { + t.Fatalf("expected ErrTFVersionMismatch, got %T, %s", err, err) + } + + // in 0.12, we just return "unknown" as the specifics are not included in the error messaging + if e.Constraint != "unknown" && e.Constraint != ">99.0.0" { + t.Fatalf("unexpected constraint %q", e.Constraint) + } + + if e.TFVersion != tfv.String() { + t.Fatalf("expected %q, got %q", tfv.String(), e.TFVersion) + } + }) +} diff --git a/tfexec/internal/e2etest/testdata/tf99/main.tf b/tfexec/internal/e2etest/testdata/tf99/main.tf new file mode 100644 index 00000000..d979cd28 --- /dev/null +++ b/tfexec/internal/e2etest/testdata/tf99/main.tf @@ -0,0 +1,3 @@ +terraform { + required_version = ">99.0.0" +}