github.com/hartzell/terraform@v0.8.6-0.20180503104400-0cc9e050ecd4/backend/testing.go (about)

     1  package backend
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"testing"
     7  
     8  	uuid "github.com/hashicorp/go-uuid"
     9  	"github.com/hashicorp/terraform/config"
    10  	"github.com/hashicorp/terraform/state"
    11  	"github.com/hashicorp/terraform/terraform"
    12  )
    13  
    14  // TestBackendConfig validates and configures the backend with the
    15  // given configuration.
    16  func TestBackendConfig(t *testing.T, b Backend, c map[string]interface{}) Backend {
    17  	t.Helper()
    18  
    19  	// Get the proper config structure
    20  	rc, err := config.NewRawConfig(c)
    21  	if err != nil {
    22  		t.Fatalf("bad: %s", err)
    23  	}
    24  	conf := terraform.NewResourceConfig(rc)
    25  
    26  	// Validate
    27  	warns, errs := b.Validate(conf)
    28  	if len(warns) > 0 {
    29  		t.Fatalf("warnings: %s", warns)
    30  	}
    31  	if len(errs) > 0 {
    32  		t.Fatalf("errors: %s", errs)
    33  	}
    34  
    35  	// Configure
    36  	if err := b.Configure(conf); err != nil {
    37  		t.Fatalf("err: %s", err)
    38  	}
    39  
    40  	return b
    41  }
    42  
    43  // TestBackend will test the functionality of a Backend. The backend is
    44  // assumed to already be configured. This will test state functionality.
    45  // If the backend reports it doesn't support multi-state by returning the
    46  // error ErrNamedStatesNotSupported, then it will not test that.
    47  func TestBackendStates(t *testing.T, b Backend) {
    48  	t.Helper()
    49  
    50  	states, err := b.States()
    51  	if err == ErrNamedStatesNotSupported {
    52  		t.Logf("TestBackend: named states not supported in %T, skipping", b)
    53  		return
    54  	}
    55  
    56  	// Test it starts with only the default
    57  	if len(states) != 1 || states[0] != DefaultStateName {
    58  		t.Fatalf("should only have default to start: %#v", states)
    59  	}
    60  
    61  	// Create a couple states
    62  	foo, err := b.State("foo")
    63  	if err != nil {
    64  		t.Fatalf("error: %s", err)
    65  	}
    66  	if err := foo.RefreshState(); err != nil {
    67  		t.Fatalf("bad: %s", err)
    68  	}
    69  	if v := foo.State(); v.HasResources() {
    70  		t.Fatalf("should be empty: %s", v)
    71  	}
    72  
    73  	bar, err := b.State("bar")
    74  	if err != nil {
    75  		t.Fatalf("error: %s", err)
    76  	}
    77  	if err := bar.RefreshState(); err != nil {
    78  		t.Fatalf("bad: %s", err)
    79  	}
    80  	if v := bar.State(); v.HasResources() {
    81  		t.Fatalf("should be empty: %s", v)
    82  	}
    83  
    84  	// Verify they are distinct states that can be read back from storage
    85  	{
    86  		// start with a fresh state, and record the lineage being
    87  		// written to "bar"
    88  		barState := terraform.NewState()
    89  
    90  		// creating the named state may have created a lineage, so use that if it exists.
    91  		if s := bar.State(); s != nil && s.Lineage != "" {
    92  			barState.Lineage = s.Lineage
    93  		}
    94  		barLineage := barState.Lineage
    95  
    96  		// the foo lineage should be distinct from bar, and unchanged after
    97  		// modifying bar
    98  		fooState := terraform.NewState()
    99  		// creating the named state may have created a lineage, so use that if it exists.
   100  		if s := foo.State(); s != nil && s.Lineage != "" {
   101  			fooState.Lineage = s.Lineage
   102  		}
   103  		fooLineage := fooState.Lineage
   104  
   105  		// write a known state to foo
   106  		if err := foo.WriteState(fooState); err != nil {
   107  			t.Fatal("error writing foo state:", err)
   108  		}
   109  		if err := foo.PersistState(); err != nil {
   110  			t.Fatal("error persisting foo state:", err)
   111  		}
   112  
   113  		// write a distinct known state to bar
   114  		if err := bar.WriteState(barState); err != nil {
   115  			t.Fatalf("bad: %s", err)
   116  		}
   117  		if err := bar.PersistState(); err != nil {
   118  			t.Fatalf("bad: %s", err)
   119  		}
   120  
   121  		// verify that foo is unchanged with the existing state manager
   122  		if err := foo.RefreshState(); err != nil {
   123  			t.Fatal("error refreshing foo:", err)
   124  		}
   125  		fooState = foo.State()
   126  		switch {
   127  		case fooState == nil:
   128  			t.Fatal("nil state read from foo")
   129  		case fooState.Lineage == barLineage:
   130  			t.Fatalf("bar lineage read from foo: %#v", fooState)
   131  		case fooState.Lineage != fooLineage:
   132  			t.Fatal("foo lineage alterred")
   133  		}
   134  
   135  		// fetch foo again from the backend
   136  		foo, err = b.State("foo")
   137  		if err != nil {
   138  			t.Fatal("error re-fetching state:", err)
   139  		}
   140  		if err := foo.RefreshState(); err != nil {
   141  			t.Fatal("error refreshing foo:", err)
   142  		}
   143  		fooState = foo.State()
   144  		switch {
   145  		case fooState == nil:
   146  			t.Fatal("nil state read from foo")
   147  		case fooState.Lineage != fooLineage:
   148  			t.Fatal("incorrect state returned from backend")
   149  		}
   150  
   151  		// fetch the bar  again from the backend
   152  		bar, err = b.State("bar")
   153  		if err != nil {
   154  			t.Fatal("error re-fetching state:", err)
   155  		}
   156  		if err := bar.RefreshState(); err != nil {
   157  			t.Fatal("error refreshing bar:", err)
   158  		}
   159  		barState = bar.State()
   160  		switch {
   161  		case barState == nil:
   162  			t.Fatal("nil state read from bar")
   163  		case barState.Lineage != barLineage:
   164  			t.Fatal("incorrect state returned from backend")
   165  		}
   166  	}
   167  
   168  	// Verify we can now list them
   169  	{
   170  		// we determined that named stated are supported earlier
   171  		states, err := b.States()
   172  		if err != nil {
   173  			t.Fatal(err)
   174  		}
   175  
   176  		sort.Strings(states)
   177  		expected := []string{"bar", "default", "foo"}
   178  		if !reflect.DeepEqual(states, expected) {
   179  			t.Fatalf("bad: %#v", states)
   180  		}
   181  	}
   182  
   183  	// Delete some states
   184  	if err := b.DeleteState("foo"); err != nil {
   185  		t.Fatalf("err: %s", err)
   186  	}
   187  
   188  	// Verify the default state can't be deleted
   189  	if err := b.DeleteState(DefaultStateName); err == nil {
   190  		t.Fatal("expected error")
   191  	}
   192  
   193  	// Create and delete the foo state again.
   194  	// Make sure that there are no leftover artifacts from a deleted state
   195  	// preventing re-creation.
   196  	foo, err = b.State("foo")
   197  	if err != nil {
   198  		t.Fatalf("error: %s", err)
   199  	}
   200  	if err := foo.RefreshState(); err != nil {
   201  		t.Fatalf("bad: %s", err)
   202  	}
   203  	if v := foo.State(); v.HasResources() {
   204  		t.Fatalf("should be empty: %s", v)
   205  	}
   206  	// and delete it again
   207  	if err := b.DeleteState("foo"); err != nil {
   208  		t.Fatalf("err: %s", err)
   209  	}
   210  
   211  	// Verify deletion
   212  	{
   213  		states, err := b.States()
   214  		if err == ErrNamedStatesNotSupported {
   215  			t.Logf("TestBackend: named states not supported in %T, skipping", b)
   216  			return
   217  		}
   218  
   219  		sort.Strings(states)
   220  		expected := []string{"bar", "default"}
   221  		if !reflect.DeepEqual(states, expected) {
   222  			t.Fatalf("bad: %#v", states)
   223  		}
   224  	}
   225  }
   226  
   227  // TestBackendStateLocks will test the locking functionality of the remote
   228  // state backend.
   229  func TestBackendStateLocks(t *testing.T, b1, b2 Backend) {
   230  	t.Helper()
   231  	testLocks(t, b1, b2, false)
   232  }
   233  
   234  // TestBackendStateForceUnlock verifies that the lock error is the expected
   235  // type, and the lock can be unlocked using the ID reported in the error.
   236  // Remote state backends that support -force-unlock should call this in at
   237  // least one of the acceptance tests.
   238  func TestBackendStateForceUnlock(t *testing.T, b1, b2 Backend) {
   239  	t.Helper()
   240  	testLocks(t, b1, b2, true)
   241  }
   242  
   243  func testLocks(t *testing.T, b1, b2 Backend, testForceUnlock bool) {
   244  	t.Helper()
   245  
   246  	// Get the default state for each
   247  	b1StateMgr, err := b1.State(DefaultStateName)
   248  	if err != nil {
   249  		t.Fatalf("error: %s", err)
   250  	}
   251  	if err := b1StateMgr.RefreshState(); err != nil {
   252  		t.Fatalf("bad: %s", err)
   253  	}
   254  
   255  	// Fast exit if this doesn't support locking at all
   256  	if _, ok := b1StateMgr.(state.Locker); !ok {
   257  		t.Logf("TestBackend: backend %T doesn't support state locking, not testing", b1)
   258  		return
   259  	}
   260  
   261  	t.Logf("TestBackend: testing state locking for %T", b1)
   262  
   263  	b2StateMgr, err := b2.State(DefaultStateName)
   264  	if err != nil {
   265  		t.Fatalf("error: %s", err)
   266  	}
   267  	if err := b2StateMgr.RefreshState(); err != nil {
   268  		t.Fatalf("bad: %s", err)
   269  	}
   270  
   271  	// Reassign so its obvious whats happening
   272  	lockerA := b1StateMgr.(state.Locker)
   273  	lockerB := b2StateMgr.(state.Locker)
   274  
   275  	infoA := state.NewLockInfo()
   276  	infoA.Operation = "test"
   277  	infoA.Who = "clientA"
   278  
   279  	infoB := state.NewLockInfo()
   280  	infoB.Operation = "test"
   281  	infoB.Who = "clientB"
   282  
   283  	lockIDA, err := lockerA.Lock(infoA)
   284  	if err != nil {
   285  		t.Fatal("unable to get initial lock:", err)
   286  	}
   287  
   288  	// Make sure we can still get the state.State from another instance even
   289  	// when locked.  This should only happen when a state is loaded via the
   290  	// backend, and as a remote state.
   291  	_, err = b2.State(DefaultStateName)
   292  	if err != nil {
   293  		t.Errorf("failed to read locked state from another backend instance: %s", err)
   294  	}
   295  
   296  	// If the lock ID is blank, assume locking is disabled
   297  	if lockIDA == "" {
   298  		t.Logf("TestBackend: %T: empty string returned for lock, assuming disabled", b1)
   299  		return
   300  	}
   301  
   302  	_, err = lockerB.Lock(infoB)
   303  	if err == nil {
   304  		lockerA.Unlock(lockIDA)
   305  		t.Fatal("client B obtained lock while held by client A")
   306  	}
   307  
   308  	if err := lockerA.Unlock(lockIDA); err != nil {
   309  		t.Fatal("error unlocking client A", err)
   310  	}
   311  
   312  	lockIDB, err := lockerB.Lock(infoB)
   313  	if err != nil {
   314  		t.Fatal("unable to obtain lock from client B")
   315  	}
   316  
   317  	if lockIDB == lockIDA {
   318  		t.Errorf("duplicate lock IDs: %q", lockIDB)
   319  	}
   320  
   321  	if err = lockerB.Unlock(lockIDB); err != nil {
   322  		t.Fatal("error unlocking client B:", err)
   323  	}
   324  
   325  	// test the equivalent of -force-unlock, by using the id from the error
   326  	// output.
   327  	if !testForceUnlock {
   328  		return
   329  	}
   330  
   331  	// get a new ID
   332  	infoA.ID, err = uuid.GenerateUUID()
   333  	if err != nil {
   334  		panic(err)
   335  	}
   336  
   337  	lockIDA, err = lockerA.Lock(infoA)
   338  	if err != nil {
   339  		t.Fatal("unable to get re lock A:", err)
   340  	}
   341  	unlock := func() {
   342  		err := lockerA.Unlock(lockIDA)
   343  		if err != nil {
   344  			t.Fatal(err)
   345  		}
   346  	}
   347  
   348  	_, err = lockerB.Lock(infoB)
   349  	if err == nil {
   350  		unlock()
   351  		t.Fatal("client B obtained lock while held by client A")
   352  	}
   353  
   354  	infoErr, ok := err.(*state.LockError)
   355  	if !ok {
   356  		unlock()
   357  		t.Fatalf("expected type *state.LockError, got : %#v", err)
   358  	}
   359  
   360  	// try to unlock with the second unlocker, using the ID from the error
   361  	if err := lockerB.Unlock(infoErr.Info.ID); err != nil {
   362  		unlock()
   363  		t.Fatalf("could not unlock with the reported ID %q: %s", infoErr.Info.ID, err)
   364  	}
   365  }