diff --git a/internal/cloud/backend.go b/internal/cloud/backend.go index 418658417..5fdd2b12c 100644 --- a/internal/cloud/backend.go +++ b/internal/cloud/backend.go @@ -784,25 +784,15 @@ func (b *Cloud) VerifyWorkspaceTerraformVersion(workspaceName string) tfdiags.Di // Even if ignoring version conflicts, it may still be useful to call this // method and warn the user about a mismatch between the local and remote // Terraform versions. - severity := tfdiags.Error - if b.ignoreVersionConflict { - severity = tfdiags.Warning - } - suggestion := " If you're sure you want to upgrade the state, you can force Terraform to continue using the -ignore-remote-version flag. This may result in an unusable workspace." - if b.ignoreVersionConflict { - suggestion = "" - } - remoteVersion, err := version.NewSemver(workspace.TerraformVersion) if err != nil { + log.Printf("[DEBUG] Invalid Terraform version (%s); will try to parse as version constraint", workspace.TerraformVersion) + } + if remoteVersion == nil { // If it's not a valid version, it might be a valid version constraint: remoteConstraint, err := version.NewConstraint(workspace.TerraformVersion) if err != nil { - diags = diags.Append(tfdiags.Sourceless( - tfdiags.Error, - "Error looking up workspace", - fmt.Sprintf("Invalid Terraform version or version constraint: %s", err), - )) + diags = diags.Append(terraformInvalidVersionOrConstraint(b.ignoreVersionConflict, workspace.TerraformVersion)) return diags } @@ -817,19 +807,7 @@ func (b *Cloud) VerifyWorkspaceTerraformVersion(workspaceName string) tfdiags.Di return diags } - diags = diags.Append(tfdiags.Sourceless( - severity, - "Terraform version mismatch", - fmt.Sprintf( - "The local Terraform version (%s) does not meet the version requirements for remote workspace %s/%s (%s).%s", - tfversion.String(), - b.organization, - workspace.Name, - workspace.TerraformVersion, - suggestion, - ), - )) - + diags = diags.Append(terraformMismatchDiagnostic(b.ignoreVersionConflict, b.organization, workspace, tfversion.String())) return diags } @@ -862,19 +840,7 @@ func (b *Cloud) VerifyWorkspaceTerraformVersion(workspaceName string) tfdiags.Di } } - diags = diags.Append(tfdiags.Sourceless( - severity, - "Terraform version mismatch", - fmt.Sprintf( - "The local Terraform version (%s) does not match the configured version for remote workspace %s/%s (%s).%s", - tfversion.String(), - b.organization, - workspace.Name, - workspace.TerraformVersion, - suggestion, - ), - )) - + diags = diags.Append(terraformMismatchDiagnostic(b.ignoreVersionConflict, b.organization, workspace, tfversion.String())) return diags } diff --git a/internal/cloud/backend_test.go b/internal/cloud/backend_test.go index 56fe9ebe1..38e4db8d6 100644 --- a/internal/cloud/backend_test.go +++ b/internal/cloud/backend_test.go @@ -705,7 +705,7 @@ func TestCloud_VerifyWorkspaceTerraformVersion_workspaceErrors(t *testing.T) { if len(diags) != 1 { t.Fatal("expected diag, but none returned") } - if got := diags.Err().Error(); !strings.Contains(got, "Error looking up workspace: Invalid Terraform version") { + if got := diags.Err().Error(); !strings.Contains(got, "Terraform version error: The remote workspace specified") { t.Fatalf("unexpected error: %s", got) } } @@ -760,7 +760,7 @@ func TestCloud_VerifyWorkspaceTerraformVersion_ignoreFlagSet(t *testing.T) { if got, want := diags[0].Description().Summary, "Terraform version mismatch"; got != want { t.Errorf("wrong summary: got %s, want %s", got, want) } - wantDetail := "The local Terraform version (0.14.0) does not match the configured version for remote workspace hashicorp/app-prod (0.13.5)." + wantDetail := "The local Terraform version (0.14.0) does not meet the version requirements for remote workspace hashicorp/app-prod (0.13.5)." if got := diags[0].Description().Detail; got != wantDetail { t.Errorf("wrong summary: got %s, want %s", got, wantDetail) } diff --git a/internal/cloud/errors.go b/internal/cloud/errors.go index 2c5910030..b51f44338 100644 --- a/internal/cloud/errors.go +++ b/internal/cloud/errors.go @@ -2,7 +2,9 @@ package cloud import ( "fmt" + "strings" + tfe "github.com/hashicorp/go-tfe" "github.com/hashicorp/terraform/internal/tfdiags" "github.com/zclconf/go-cty/cty" ) @@ -29,3 +31,42 @@ var ( cty.Path{cty.GetAttrStep{Name: "workspaces"}}, ) ) + +func terraformMismatchDiagnostic(ignoreVersionConflict bool, organization string, workspace *tfe.Workspace, tfversion string) tfdiags.Diagnostic { + severity := tfdiags.Error + if ignoreVersionConflict { + severity = tfdiags.Warning + } + + suggestion := "If you're sure you want to upgrade the state, you can force Terraform to continue using the -ignore-remote-version flag. This may result in an unusable workspace." + if ignoreVersionConflict { + suggestion = "" + } + + description := fmt.Sprintf( + "The local Terraform version (%s) does not meet the version requirements for remote workspace %s/%s (%s).\n\n%s", + tfversion, + organization, + workspace.Name, + workspace.TerraformVersion, + suggestion, + ) + description = strings.TrimSpace(description) + return tfdiags.Sourceless(severity, "Terraform version mismatch", description) +} + +func terraformInvalidVersionOrConstraint(ignoreVersionConflict bool, tfversion string) tfdiags.Diagnostic { + severity := tfdiags.Error + if ignoreVersionConflict { + severity = tfdiags.Warning + } + + suggestion := "If you're sure you want to upgrade the state, you can force Terraform to continue using the -ignore-remote-version flag. This may result in an unusable workspace." + if ignoreVersionConflict { + suggestion = "" + } + + description := fmt.Sprintf("The remote workspace specified an invalid Terraform version or version constraint: %s\n\n%s", tfversion, suggestion) + description = strings.TrimSpace(description) + return tfdiags.Sourceless(severity, "Terraform version error", description) +}