@ -1,13 +1,18 @@
package remote
import (
"context"
"fmt"
"reflect"
"strings"
"testing"
tfe "github.com/hashicorp/go-tfe"
version "github.com/hashicorp/go-version"
"github.com/hashicorp/terraform-svchost/disco"
"github.com/hashicorp/terraform/backend"
"github.com/hashicorp/terraform/version"
"github.com/hashicorp/terraform/tfdiags"
tfversion "github.com/hashicorp/terraform/version"
"github.com/zclconf/go-cty/cty"
backendLocal "github.com/hashicorp/terraform/backend/local"
@ -196,11 +201,11 @@ func TestRemote_versionConstraints(t *testing.T) {
}
// Save and restore the actual version.
p := version. Prerelease
v := version. Version
p := tf version. Prerelease
v := tf version. Version
defer func ( ) {
version. Prerelease = p
version. Version = v
tf version. Prerelease = p
tf version. Version = v
} ( )
for name , tc := range cases {
@ -208,8 +213,8 @@ func TestRemote_versionConstraints(t *testing.T) {
b := New ( testDisco ( s ) )
// Set the version for this test.
version. Prerelease = tc . prerelease
version. Version = tc . version
tf version. Prerelease = tc . prerelease
tf version. Version = tc . version
// Validate
_ , valDiags := b . PrepareConfig ( tc . config )
@ -428,17 +433,17 @@ func TestRemote_checkConstraints(t *testing.T) {
}
// Save and restore the actual version.
p := version. Prerelease
v := version. Version
p := tf version. Prerelease
v := tf version. Version
defer func ( ) {
version. Prerelease = p
version. Version = v
tf version. Prerelease = p
tf version. Version = v
} ( )
for name , tc := range cases {
// Set the version for this test.
version. Prerelease = tc . prerelease
version. Version = tc . version
tf version. Prerelease = tc . prerelease
tf version. Version = tc . version
// Check the constraints.
diags := b . checkConstraints ( tc . constraints )
@ -448,3 +453,222 @@ func TestRemote_checkConstraints(t *testing.T) {
}
}
}
func TestRemote_StateMgr_versionCheck ( t * testing . T ) {
b , bCleanup := testBackendDefault ( t )
defer bCleanup ( )
// Some fixed versions for testing with. This logic is a simple string
// comparison, so we don't need many test cases.
v0135 := version . Must ( version . NewSemver ( "0.13.5" ) )
v0140 := version . Must ( version . NewSemver ( "0.14.0" ) )
// Save original local version state and restore afterwards
p := tfversion . Prerelease
v := tfversion . Version
s := tfversion . SemVer
defer func ( ) {
tfversion . Prerelease = p
tfversion . Version = v
tfversion . SemVer = s
} ( )
// For this test, the local Terraform version is set to 0.14.0
tfversion . Prerelease = ""
tfversion . Version = v0140 . String ( )
tfversion . SemVer = v0140
// Update the mock remote workspace Terraform version to match the local
// Terraform version
if _ , err := b . client . Workspaces . Update (
context . Background ( ) ,
b . organization ,
b . workspace ,
tfe . WorkspaceUpdateOptions {
TerraformVersion : tfe . String ( v0140 . String ( ) ) ,
} ,
) ; err != nil {
t . Fatalf ( "error: %v" , err )
}
// This should succeed
if _ , err := b . StateMgr ( backend . DefaultStateName ) ; err != nil {
t . Fatalf ( "expected no error, got %v" , err )
}
// Now change the remote workspace to a different Terraform version
if _ , err := b . client . Workspaces . Update (
context . Background ( ) ,
b . organization ,
b . workspace ,
tfe . WorkspaceUpdateOptions {
TerraformVersion : tfe . String ( v0135 . String ( ) ) ,
} ,
) ; err != nil {
t . Fatalf ( "error: %v" , err )
}
// This should fail
want := ` Remote workspace Terraform version "0.13.5" does not match local Terraform version "0.14.0" `
if _ , err := b . StateMgr ( backend . DefaultStateName ) ; err . Error ( ) != want {
t . Fatalf ( "wrong error\n got: %v\nwant: %v" , err . Error ( ) , want )
}
}
func TestRemote_VerifyWorkspaceTerraformVersion ( t * testing . T ) {
testCases := [ ] struct {
local string
remote string
wantErr bool
} {
{ "0.13.5" , "0.13.5" , false } ,
{ "0.14.0" , "0.13.5" , true } ,
{ "0.14.0" , "0.14.1" , false } ,
{ "0.14.0" , "1.0.99" , false } ,
{ "0.14.0" , "1.1.0" , true } ,
{ "1.2.0" , "1.2.99" , false } ,
{ "1.2.0" , "1.3.0" , true } ,
}
for _ , tc := range testCases {
t . Run ( fmt . Sprintf ( "local %s, remote %s" , tc . local , tc . remote ) , func ( t * testing . T ) {
b , bCleanup := testBackendDefault ( t )
defer bCleanup ( )
local := version . Must ( version . NewSemver ( tc . local ) )
remote := version . Must ( version . NewSemver ( tc . remote ) )
// Save original local version state and restore afterwards
p := tfversion . Prerelease
v := tfversion . Version
s := tfversion . SemVer
defer func ( ) {
tfversion . Prerelease = p
tfversion . Version = v
tfversion . SemVer = s
} ( )
// Override local version as specified
tfversion . Prerelease = ""
tfversion . Version = local . String ( )
tfversion . SemVer = local
// Update the mock remote workspace Terraform version to the
// specified remote version
if _ , err := b . client . Workspaces . Update (
context . Background ( ) ,
b . organization ,
b . workspace ,
tfe . WorkspaceUpdateOptions {
TerraformVersion : tfe . String ( remote . String ( ) ) ,
} ,
) ; err != nil {
t . Fatalf ( "error: %v" , err )
}
diags := b . VerifyWorkspaceTerraformVersion ( backend . DefaultStateName )
if tc . wantErr {
if len ( diags ) != 1 {
t . Fatal ( "expected diag, but none returned" )
}
if got := diags . Err ( ) . Error ( ) ; ! strings . Contains ( got , "Terraform version mismatch" ) {
t . Fatalf ( "unexpected error: %s" , got )
}
} else {
if len ( diags ) != 0 {
t . Fatalf ( "unexpected diags: %s" , diags . Err ( ) )
}
}
} )
}
}
func TestRemote_VerifyWorkspaceTerraformVersion_workspaceErrors ( t * testing . T ) {
b , bCleanup := testBackendDefault ( t )
defer bCleanup ( )
// Attempting to check the version against a workspace which doesn't exist
// should fail
diags := b . VerifyWorkspaceTerraformVersion ( "invalid-workspace" )
if len ( diags ) != 1 {
t . Fatal ( "expected diag, but none returned" )
}
if got := diags . Err ( ) . Error ( ) ; ! strings . Contains ( got , "Error looking up workspace: Workspace read failed" ) {
t . Fatalf ( "unexpected error: %s" , got )
}
// Update the mock remote workspace Terraform version to an invalid version
if _ , err := b . client . Workspaces . Update (
context . Background ( ) ,
b . organization ,
b . workspace ,
tfe . WorkspaceUpdateOptions {
TerraformVersion : tfe . String ( "1.0.cheetarah" ) ,
} ,
) ; err != nil {
t . Fatalf ( "error: %v" , err )
}
diags = b . VerifyWorkspaceTerraformVersion ( backend . DefaultStateName )
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" ) {
t . Fatalf ( "unexpected error: %s" , got )
}
}
func TestRemote_VerifyWorkspaceTerraformVersion_ignoreFlagSet ( t * testing . T ) {
b , bCleanup := testBackendDefault ( t )
defer bCleanup ( )
// If the ignore flag is set, the behaviour changes
b . IgnoreVersionConflict ( )
// Different local & remote versions to cause an error
local := version . Must ( version . NewSemver ( "0.14.0" ) )
remote := version . Must ( version . NewSemver ( "0.13.5" ) )
// Save original local version state and restore afterwards
p := tfversion . Prerelease
v := tfversion . Version
s := tfversion . SemVer
defer func ( ) {
tfversion . Prerelease = p
tfversion . Version = v
tfversion . SemVer = s
} ( )
// Override local version as specified
tfversion . Prerelease = ""
tfversion . Version = local . String ( )
tfversion . SemVer = local
// Update the mock remote workspace Terraform version to the
// specified remote version
if _ , err := b . client . Workspaces . Update (
context . Background ( ) ,
b . organization ,
b . workspace ,
tfe . WorkspaceUpdateOptions {
TerraformVersion : tfe . String ( remote . String ( ) ) ,
} ,
) ; err != nil {
t . Fatalf ( "error: %v" , err )
}
diags := b . VerifyWorkspaceTerraformVersion ( backend . DefaultStateName )
if len ( diags ) != 1 {
t . Fatal ( "expected diag, but none returned" )
}
if got , want := diags [ 0 ] . Severity ( ) , tfdiags . Warning ; got != want {
t . Errorf ( "wrong severity: got %#v, want %#v" , got , want )
}
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/prod (0.13.5)."
if got := diags [ 0 ] . Description ( ) . Detail ; got != wantDetail {
t . Errorf ( "wrong summary: got %s, want %s" , got , wantDetail )
}
}