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