github.com/tompao/terraform@v0.6.10-0.20180215233341-e41b29d0961b/backend/testing.go (about)

     1  package backend
     2  
     3  import (
     4  	"reflect"
     5  	"sort"
     6  	"testing"
     7  
     8  	"github.com/hashicorp/terraform/config"
     9  	"github.com/hashicorp/terraform/state"
    10  	"github.com/hashicorp/terraform/terraform"
    11  )
    12  
    13  // TestBackendConfig validates and configures the backend with the
    14  // given configuration.
    15  func TestBackendConfig(t *testing.T, b Backend, c map[string]interface{}) Backend {
    16  	t.Helper()
    17  
    18  	// Get the proper config structure
    19  	rc, err := config.NewRawConfig(c)
    20  	if err != nil {
    21  		t.Fatalf("bad: %s", err)
    22  	}
    23  	conf := terraform.NewResourceConfig(rc)
    24  
    25  	// Validate
    26  	warns, errs := b.Validate(conf)
    27  	if len(warns) > 0 {
    28  		t.Fatalf("warnings: %s", warns)
    29  	}
    30  	if len(errs) > 0 {
    31  		t.Fatalf("errors: %s", errs)
    32  	}
    33  
    34  	// Configure
    35  	if err := b.Configure(conf); err != nil {
    36  		t.Fatalf("err: %s", err)
    37  	}
    38  
    39  	return b
    40  }
    41  
    42  // TestBackend will test the functionality of a Backend. The backend is
    43  // assumed to already be configured. This will test state functionality.
    44  // If the backend reports it doesn't support multi-state by returning the
    45  // error ErrNamedStatesNotSupported, then it will not test that.
    46  //
    47  // If you want to test locking, two backends must be given. If b2 is nil,
    48  // then state locking won't be tested.
    49  func TestBackend(t *testing.T, b1, b2 Backend) {
    50  	t.Helper()
    51  
    52  	testBackendStates(t, b1)
    53  
    54  	if b2 != nil {
    55  		testBackendStateLock(t, b1, b2)
    56  	}
    57  }
    58  
    59  func testBackendStates(t *testing.T, b Backend) {
    60  	t.Helper()
    61  
    62  	states, err := b.States()
    63  	if err == ErrNamedStatesNotSupported {
    64  		t.Logf("TestBackend: named states not supported in %T, skipping", b)
    65  		return
    66  	}
    67  
    68  	// Test it starts with only the default
    69  	if len(states) != 1 || states[0] != DefaultStateName {
    70  		t.Fatalf("should only 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 !reflect.DeepEqual(states, expected) {
   191  			t.Fatalf("bad: %#v", states)
   192  		}
   193  	}
   194  
   195  	// Delete some states
   196  	if err := b.DeleteState("foo"); err != nil {
   197  		t.Fatalf("err: %s", err)
   198  	}
   199  
   200  	// Verify the default state can't be deleted
   201  	if err := b.DeleteState(DefaultStateName); err == nil {
   202  		t.Fatal("expected error")
   203  	}
   204  
   205  	// Create and delete the foo state again.
   206  	// Make sure that there are no leftover artifacts from a deleted state
   207  	// preventing re-creation.
   208  	foo, err = b.State("foo")
   209  	if err != nil {
   210  		t.Fatalf("error: %s", err)
   211  	}
   212  	if err := foo.RefreshState(); err != nil {
   213  		t.Fatalf("bad: %s", err)
   214  	}
   215  	if v := foo.State(); v.HasResources() {
   216  		t.Fatalf("should be empty: %s", v)
   217  	}
   218  	// and delete it again
   219  	if err := b.DeleteState("foo"); err != nil {
   220  		t.Fatalf("err: %s", err)
   221  	}
   222  
   223  	// Verify deletion
   224  	{
   225  		states, err := b.States()
   226  		if err == ErrNamedStatesNotSupported {
   227  			t.Logf("TestBackend: named states not supported in %T, skipping", b)
   228  			return
   229  		}
   230  
   231  		sort.Strings(states)
   232  		expected := []string{"bar", "default"}
   233  		if !reflect.DeepEqual(states, expected) {
   234  			t.Fatalf("bad: %#v", states)
   235  		}
   236  	}
   237  }
   238  
   239  func testBackendStateLock(t *testing.T, b1, b2 Backend) {
   240  	t.Helper()
   241  
   242  	// Get the default state for each
   243  	b1StateMgr, err := b1.State(DefaultStateName)
   244  	if err != nil {
   245  		t.Fatalf("error: %s", err)
   246  	}
   247  	if err := b1StateMgr.RefreshState(); err != nil {
   248  		t.Fatalf("bad: %s", err)
   249  	}
   250  
   251  	// Fast exit if this doesn't support locking at all
   252  	if _, ok := b1StateMgr.(state.Locker); !ok {
   253  		t.Logf("TestBackend: backend %T doesn't support state locking, not testing", b1)
   254  		return
   255  	}
   256  
   257  	t.Logf("TestBackend: testing state locking for %T", b1)
   258  
   259  	b2StateMgr, err := b2.State(DefaultStateName)
   260  	if err != nil {
   261  		t.Fatalf("error: %s", err)
   262  	}
   263  	if err := b2StateMgr.RefreshState(); err != nil {
   264  		t.Fatalf("bad: %s", err)
   265  	}
   266  
   267  	// Reassign so its obvious whats happening
   268  	lockerA := b1StateMgr.(state.Locker)
   269  	lockerB := b2StateMgr.(state.Locker)
   270  
   271  	infoA := state.NewLockInfo()
   272  	infoA.Operation = "test"
   273  	infoA.Who = "clientA"
   274  
   275  	infoB := state.NewLockInfo()
   276  	infoB.Operation = "test"
   277  	infoB.Who = "clientB"
   278  
   279  	lockIDA, err := lockerA.Lock(infoA)
   280  	if err != nil {
   281  		t.Fatal("unable to get initial lock:", err)
   282  	}
   283  
   284  	// Make sure we can still get the state.State from another instance even
   285  	// when locked.  This should only happen when a state is loaded via the
   286  	// backend, and as a remote state.
   287  	_, err = b2.State(DefaultStateName)
   288  	if err != nil {
   289  		t.Fatalf("failed to read locked state from another backend instance: %s", err)
   290  	}
   291  
   292  	// If the lock ID is blank, assume locking is disabled
   293  	if lockIDA == "" {
   294  		t.Logf("TestBackend: %T: empty string returned for lock, assuming disabled", b1)
   295  		return
   296  	}
   297  
   298  	_, err = lockerB.Lock(infoB)
   299  	if err == nil {
   300  		lockerA.Unlock(lockIDA)
   301  		t.Fatal("client B obtained lock while held by client A")
   302  	}
   303  
   304  	if err := lockerA.Unlock(lockIDA); err != nil {
   305  		t.Fatal("error unlocking client A", err)
   306  	}
   307  
   308  	lockIDB, err := lockerB.Lock(infoB)
   309  	if err != nil {
   310  		t.Fatal("unable to obtain lock from client B")
   311  	}
   312  
   313  	if lockIDB == lockIDA {
   314  		t.Fatalf("duplicate lock IDs: %q", lockIDB)
   315  	}
   316  
   317  	if err = lockerB.Unlock(lockIDB); err != nil {
   318  		t.Fatal("error unlocking client B:", err)
   319  	}
   320  
   321  }