github.com/richardmarshall/terraform@v0.9.5-0.20170429023105-15704cc6ee35/backend/local/backend_test.go (about)

     1  package local
     2  
     3  import (
     4  	"errors"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/hashicorp/terraform/backend"
    13  	"github.com/hashicorp/terraform/state"
    14  	"github.com/hashicorp/terraform/terraform"
    15  )
    16  
    17  func TestLocal_impl(t *testing.T) {
    18  	var _ backend.Enhanced = new(Local)
    19  	var _ backend.Local = new(Local)
    20  	var _ backend.CLI = new(Local)
    21  }
    22  
    23  func TestLocal_backend(t *testing.T) {
    24  	defer testTmpDir(t)()
    25  	b := &Local{}
    26  	backend.TestBackend(t, b, b)
    27  }
    28  
    29  func checkState(t *testing.T, path, expected string) {
    30  	// Read the state
    31  	f, err := os.Open(path)
    32  	if err != nil {
    33  		t.Fatalf("err: %s", err)
    34  	}
    35  
    36  	state, err := terraform.ReadState(f)
    37  	f.Close()
    38  	if err != nil {
    39  		t.Fatalf("err: %s", err)
    40  	}
    41  
    42  	actual := strings.TrimSpace(state.String())
    43  	expected = strings.TrimSpace(expected)
    44  	if actual != expected {
    45  		t.Fatalf("state does not match! actual:\n%s\n\nexpected:\n%s", actual, expected)
    46  	}
    47  }
    48  
    49  func TestLocal_StatePaths(t *testing.T) {
    50  	b := &Local{}
    51  
    52  	// Test the defaults
    53  	path, out, back := b.StatePaths("")
    54  
    55  	if path != DefaultStateFilename {
    56  		t.Fatalf("expected %q, got %q", DefaultStateFilename, path)
    57  	}
    58  
    59  	if out != DefaultStateFilename {
    60  		t.Fatalf("expected %q, got %q", DefaultStateFilename, out)
    61  	}
    62  
    63  	dfltBackup := DefaultStateFilename + DefaultBackupExtension
    64  	if back != dfltBackup {
    65  		t.Fatalf("expected %q, got %q", dfltBackup, back)
    66  	}
    67  
    68  	// check with env
    69  	testEnv := "test_env"
    70  	path, out, back = b.StatePaths(testEnv)
    71  
    72  	expectedPath := filepath.Join(DefaultEnvDir, testEnv, DefaultStateFilename)
    73  	expectedOut := expectedPath
    74  	expectedBackup := expectedPath + DefaultBackupExtension
    75  
    76  	if path != expectedPath {
    77  		t.Fatalf("expected %q, got %q", expectedPath, path)
    78  	}
    79  
    80  	if out != expectedOut {
    81  		t.Fatalf("expected %q, got %q", expectedOut, out)
    82  	}
    83  
    84  	if back != expectedBackup {
    85  		t.Fatalf("expected %q, got %q", expectedBackup, back)
    86  	}
    87  
    88  }
    89  
    90  func TestLocal_addAndRemoveStates(t *testing.T) {
    91  	defer testTmpDir(t)()
    92  	dflt := backend.DefaultStateName
    93  	expectedStates := []string{dflt}
    94  
    95  	b := &Local{}
    96  	states, err := b.States()
    97  	if err != nil {
    98  		t.Fatal(err)
    99  	}
   100  
   101  	if !reflect.DeepEqual(states, expectedStates) {
   102  		t.Fatalf("expected []string{%q}, got %q", dflt, states)
   103  	}
   104  
   105  	expectedA := "test_A"
   106  	if _, err := b.State(expectedA); err != nil {
   107  		t.Fatal(err)
   108  	}
   109  
   110  	states, err = b.States()
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  
   115  	expectedStates = append(expectedStates, expectedA)
   116  	if !reflect.DeepEqual(states, expectedStates) {
   117  		t.Fatalf("expected %q, got %q", expectedStates, states)
   118  	}
   119  
   120  	expectedB := "test_B"
   121  	if _, err := b.State(expectedB); err != nil {
   122  		t.Fatal(err)
   123  	}
   124  
   125  	states, err = b.States()
   126  	if err != nil {
   127  		t.Fatal(err)
   128  	}
   129  
   130  	expectedStates = append(expectedStates, expectedB)
   131  	if !reflect.DeepEqual(states, expectedStates) {
   132  		t.Fatalf("expected %q, got %q", expectedStates, states)
   133  	}
   134  
   135  	if err := b.DeleteState(expectedA); err != nil {
   136  		t.Fatal(err)
   137  	}
   138  
   139  	states, err = b.States()
   140  	if err != nil {
   141  		t.Fatal(err)
   142  	}
   143  
   144  	expectedStates = []string{dflt, expectedB}
   145  	if !reflect.DeepEqual(states, expectedStates) {
   146  		t.Fatalf("expected %q, got %q", expectedStates, states)
   147  	}
   148  
   149  	if err := b.DeleteState(expectedB); err != nil {
   150  		t.Fatal(err)
   151  	}
   152  
   153  	states, err = b.States()
   154  	if err != nil {
   155  		t.Fatal(err)
   156  	}
   157  
   158  	expectedStates = []string{dflt}
   159  	if !reflect.DeepEqual(states, expectedStates) {
   160  		t.Fatalf("expected %q, got %q", expectedStates, states)
   161  	}
   162  
   163  	if err := b.DeleteState(dflt); err == nil {
   164  		t.Fatal("expected error deleting default state")
   165  	}
   166  }
   167  
   168  // a local backend which returns sentinel errors for NamedState methods to
   169  // verify it's being called.
   170  type testDelegateBackend struct {
   171  	*Local
   172  
   173  	// return a sentinel error on these calls
   174  	stateErr  bool
   175  	statesErr bool
   176  	deleteErr bool
   177  }
   178  
   179  var errTestDelegateState = errors.New("State called")
   180  var errTestDelegateStates = errors.New("States called")
   181  var errTestDelegateDeleteState = errors.New("Delete called")
   182  
   183  func (b *testDelegateBackend) State(name string) (state.State, error) {
   184  	if b.stateErr {
   185  		return nil, errTestDelegateState
   186  	}
   187  	s := &state.LocalState{
   188  		Path:    "terraform.tfstate",
   189  		PathOut: "terraform.tfstate",
   190  	}
   191  	return s, nil
   192  }
   193  
   194  func (b *testDelegateBackend) States() ([]string, error) {
   195  	if b.statesErr {
   196  		return nil, errTestDelegateStates
   197  	}
   198  	return []string{"default"}, nil
   199  }
   200  
   201  func (b *testDelegateBackend) DeleteState(name string) error {
   202  	if b.deleteErr {
   203  		return errTestDelegateDeleteState
   204  	}
   205  	return nil
   206  }
   207  
   208  // verify that the MultiState methods are dispatched to the correct Backend.
   209  func TestLocal_multiStateBackend(t *testing.T) {
   210  	// assign a separate backend where we can read the state
   211  	b := &Local{
   212  		Backend: &testDelegateBackend{
   213  			stateErr:  true,
   214  			statesErr: true,
   215  			deleteErr: true,
   216  		},
   217  	}
   218  
   219  	if _, err := b.State("test"); err != errTestDelegateState {
   220  		t.Fatal("expected errTestDelegateState, got:", err)
   221  	}
   222  
   223  	if _, err := b.States(); err != errTestDelegateStates {
   224  		t.Fatal("expected errTestDelegateStates, got:", err)
   225  	}
   226  
   227  	if err := b.DeleteState("test"); err != errTestDelegateDeleteState {
   228  		t.Fatal("expected errTestDelegateDeleteState, got:", err)
   229  	}
   230  }
   231  
   232  // verify that a remote state backend is always wrapped in a BackupState
   233  func TestLocal_remoteStateBackup(t *testing.T) {
   234  	// assign a separate backend to mock a remote state backend
   235  	b := &Local{
   236  		Backend: &testDelegateBackend{},
   237  	}
   238  
   239  	s, err := b.State("default")
   240  	if err != nil {
   241  		t.Fatal(err)
   242  	}
   243  
   244  	bs, ok := s.(*state.BackupState)
   245  	if !ok {
   246  		t.Fatal("remote state is not backed up")
   247  	}
   248  
   249  	if bs.Path != DefaultStateFilename+DefaultBackupExtension {
   250  		t.Fatal("bad backup location:", bs.Path)
   251  	}
   252  
   253  	// do the same with a named state, which should use the local env directories
   254  	s, err = b.State("test")
   255  	if err != nil {
   256  		t.Fatal(err)
   257  	}
   258  
   259  	bs, ok = s.(*state.BackupState)
   260  	if !ok {
   261  		t.Fatal("remote state is not backed up")
   262  	}
   263  
   264  	if bs.Path != filepath.Join(DefaultEnvDir, "test", DefaultStateFilename+DefaultBackupExtension) {
   265  		t.Fatal("bad backup location:", bs.Path)
   266  	}
   267  }
   268  
   269  // change into a tmp dir and return a deferable func to change back and cleanup
   270  func testTmpDir(t *testing.T) func() {
   271  	tmp, err := ioutil.TempDir("", "tf")
   272  	if err != nil {
   273  		t.Fatal(err)
   274  	}
   275  
   276  	old, err := os.Getwd()
   277  	if err != nil {
   278  		t.Fatal(err)
   279  	}
   280  
   281  	if err := os.Chdir(tmp); err != nil {
   282  		t.Fatal(err)
   283  	}
   284  
   285  	return func() {
   286  		// ignore errors and try to clean up
   287  		os.Chdir(old)
   288  		os.RemoveAll(tmp)
   289  	}
   290  }