mirror of
https://github.com/opentofu/opentofu.git
synced 2024-12-27 09:21:14 -06:00
250 lines
5.5 KiB
Go
250 lines
5.5 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: MPL-2.0
|
|
|
|
package local
|
|
|
|
import (
|
|
"errors"
|
|
"os"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/opentofu/opentofu/internal/backend"
|
|
"github.com/opentofu/opentofu/internal/states/statefile"
|
|
"github.com/opentofu/opentofu/internal/states/statemgr"
|
|
)
|
|
|
|
func TestLocal_impl(t *testing.T) {
|
|
var _ backend.Enhanced = New()
|
|
var _ backend.Local = New()
|
|
var _ backend.CLI = New()
|
|
}
|
|
|
|
func TestLocal_backend(t *testing.T) {
|
|
testTmpDir(t)
|
|
b := New()
|
|
backend.TestBackendStates(t, b)
|
|
backend.TestBackendStateLocks(t, b, b)
|
|
}
|
|
|
|
func checkState(t *testing.T, path, expected string) {
|
|
t.Helper()
|
|
// Read the state
|
|
f, err := os.Open(path)
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
state, err := statefile.Read(f)
|
|
f.Close()
|
|
if err != nil {
|
|
t.Fatalf("err: %s", err)
|
|
}
|
|
|
|
actual := state.State.String()
|
|
expected = strings.TrimSpace(expected)
|
|
if actual != expected {
|
|
t.Fatalf("state does not match! actual:\n%s\n\nexpected:\n%s", actual, expected)
|
|
}
|
|
}
|
|
|
|
func TestLocal_StatePaths(t *testing.T) {
|
|
b := New()
|
|
|
|
// Test the defaults
|
|
path, out, back := b.StatePaths("")
|
|
|
|
if path != DefaultStateFilename {
|
|
t.Fatalf("expected %q, got %q", DefaultStateFilename, path)
|
|
}
|
|
|
|
if out != DefaultStateFilename {
|
|
t.Fatalf("expected %q, got %q", DefaultStateFilename, out)
|
|
}
|
|
|
|
dfltBackup := DefaultStateFilename + DefaultBackupExtension
|
|
if back != dfltBackup {
|
|
t.Fatalf("expected %q, got %q", dfltBackup, back)
|
|
}
|
|
|
|
// check with env
|
|
testEnv := "test_env"
|
|
path, out, back = b.StatePaths(testEnv)
|
|
|
|
expectedPath := filepath.Join(DefaultWorkspaceDir, testEnv, DefaultStateFilename)
|
|
expectedOut := expectedPath
|
|
expectedBackup := expectedPath + DefaultBackupExtension
|
|
|
|
if path != expectedPath {
|
|
t.Fatalf("expected %q, got %q", expectedPath, path)
|
|
}
|
|
|
|
if out != expectedOut {
|
|
t.Fatalf("expected %q, got %q", expectedOut, out)
|
|
}
|
|
|
|
if back != expectedBackup {
|
|
t.Fatalf("expected %q, got %q", expectedBackup, back)
|
|
}
|
|
|
|
}
|
|
|
|
func TestLocal_addAndRemoveStates(t *testing.T) {
|
|
testTmpDir(t)
|
|
dflt := backend.DefaultStateName
|
|
expectedStates := []string{dflt}
|
|
|
|
b := New()
|
|
states, err := b.Workspaces()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(states, expectedStates) {
|
|
t.Fatalf("expected []string{%q}, got %q", dflt, states)
|
|
}
|
|
|
|
expectedA := "test_A"
|
|
if _, err := b.StateMgr(expectedA); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
states, err = b.Workspaces()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedStates = append(expectedStates, expectedA)
|
|
if !reflect.DeepEqual(states, expectedStates) {
|
|
t.Fatalf("expected %q, got %q", expectedStates, states)
|
|
}
|
|
|
|
expectedB := "test_B"
|
|
if _, err := b.StateMgr(expectedB); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
states, err = b.Workspaces()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedStates = append(expectedStates, expectedB)
|
|
if !reflect.DeepEqual(states, expectedStates) {
|
|
t.Fatalf("expected %q, got %q", expectedStates, states)
|
|
}
|
|
|
|
if err := b.DeleteWorkspace(expectedA, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
states, err = b.Workspaces()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedStates = []string{dflt, expectedB}
|
|
if !reflect.DeepEqual(states, expectedStates) {
|
|
t.Fatalf("expected %q, got %q", expectedStates, states)
|
|
}
|
|
|
|
if err := b.DeleteWorkspace(expectedB, true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
states, err = b.Workspaces()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
expectedStates = []string{dflt}
|
|
if !reflect.DeepEqual(states, expectedStates) {
|
|
t.Fatalf("expected %q, got %q", expectedStates, states)
|
|
}
|
|
|
|
if err := b.DeleteWorkspace(dflt, true); err == nil {
|
|
t.Fatal("expected error deleting default state")
|
|
}
|
|
}
|
|
|
|
// a local backend which returns sentinel errors for NamedState methods to
|
|
// verify it's being called.
|
|
type testDelegateBackend struct {
|
|
*Local
|
|
|
|
// return a sentinel error on these calls
|
|
stateErr bool
|
|
statesErr bool
|
|
deleteErr bool
|
|
}
|
|
|
|
var errTestDelegateState = errors.New("state called")
|
|
var errTestDelegateStates = errors.New("states called")
|
|
var errTestDelegateDeleteState = errors.New("delete called")
|
|
|
|
func (b *testDelegateBackend) StateMgr(name string) (statemgr.Full, error) {
|
|
if b.stateErr {
|
|
return nil, errTestDelegateState
|
|
}
|
|
s := statemgr.NewFilesystem("terraform.tfstate")
|
|
return s, nil
|
|
}
|
|
|
|
func (b *testDelegateBackend) Workspaces() ([]string, error) {
|
|
if b.statesErr {
|
|
return nil, errTestDelegateStates
|
|
}
|
|
return []string{"default"}, nil
|
|
}
|
|
|
|
func (b *testDelegateBackend) DeleteWorkspace(name string, force bool) error {
|
|
if b.deleteErr {
|
|
return errTestDelegateDeleteState
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// verify that the MultiState methods are dispatched to the correct Backend.
|
|
func TestLocal_multiStateBackend(t *testing.T) {
|
|
// assign a separate backend where we can read the state
|
|
b := NewWithBackend(&testDelegateBackend{
|
|
stateErr: true,
|
|
statesErr: true,
|
|
deleteErr: true,
|
|
})
|
|
|
|
if _, err := b.StateMgr("test"); err != errTestDelegateState {
|
|
t.Fatal("expected errTestDelegateState, got:", err)
|
|
}
|
|
|
|
if _, err := b.Workspaces(); err != errTestDelegateStates {
|
|
t.Fatal("expected errTestDelegateStates, got:", err)
|
|
}
|
|
|
|
if err := b.DeleteWorkspace("test", true); err != errTestDelegateDeleteState {
|
|
t.Fatal("expected errTestDelegateDeleteState, got:", err)
|
|
}
|
|
}
|
|
|
|
// testTmpDir changes into a tmp dir and change back automatically when the test
|
|
// and all its subtests complete.
|
|
func testTmpDir(t *testing.T) {
|
|
tmp := t.TempDir()
|
|
|
|
old, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if err := os.Chdir(tmp); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
t.Cleanup(func() {
|
|
// ignore errors and try to clean up
|
|
os.Chdir(old)
|
|
})
|
|
}
|