github.com/magodo/terraform@v0.11.12-beta1/state/local_test.go (about)

     1  package state
     2  
     3  import (
     4  	"io/ioutil"
     5  	"os"
     6  	"os/exec"
     7  	"sync"
     8  	"testing"
     9  
    10  	"github.com/hashicorp/terraform/terraform"
    11  )
    12  
    13  func TestLocalState(t *testing.T) {
    14  	ls := testLocalState(t)
    15  	defer os.Remove(ls.Path)
    16  	TestState(t, ls)
    17  }
    18  
    19  func TestLocalStateRace(t *testing.T) {
    20  	ls := testLocalState(t)
    21  	defer os.Remove(ls.Path)
    22  
    23  	current := TestStateInitial()
    24  
    25  	var wg sync.WaitGroup
    26  	for i := 0; i < 100; i++ {
    27  		wg.Add(1)
    28  		go func() {
    29  			defer wg.Done()
    30  			ls.WriteState(current)
    31  		}()
    32  	}
    33  }
    34  
    35  func TestLocalStateLocks(t *testing.T) {
    36  	s := testLocalState(t)
    37  	defer os.Remove(s.Path)
    38  
    39  	// lock first
    40  	info := NewLockInfo()
    41  	info.Operation = "test"
    42  	lockID, err := s.Lock(info)
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	out, err := exec.Command("go", "run", "testdata/lockstate.go", s.Path).CombinedOutput()
    48  	if err != nil {
    49  		t.Fatal("unexpected lock failure", err, string(out))
    50  	}
    51  
    52  	if string(out) != "lock failed" {
    53  		t.Fatal("expected 'locked failed', got", string(out))
    54  	}
    55  
    56  	// check our lock info
    57  	lockInfo, err := s.lockInfo()
    58  	if err != nil {
    59  		t.Fatal(err)
    60  	}
    61  
    62  	if lockInfo.Operation != "test" {
    63  		t.Fatalf("invalid lock info %#v\n", lockInfo)
    64  	}
    65  
    66  	// a noop, since we unlock on exit
    67  	if err := s.Unlock(lockID); err != nil {
    68  		t.Fatal(err)
    69  	}
    70  
    71  	// local locks can re-lock
    72  	lockID, err = s.Lock(info)
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	if err := s.Unlock(lockID); err != nil {
    78  		t.Fatal(err)
    79  	}
    80  
    81  	// we should not be able to unlock the same lock twice
    82  	if err := s.Unlock(lockID); err == nil {
    83  		t.Fatal("unlocking an unlocked state should fail")
    84  	}
    85  
    86  	// make sure lock info is gone
    87  	lockInfoPath := s.lockInfoPath()
    88  	if _, err := os.Stat(lockInfoPath); !os.IsNotExist(err) {
    89  		t.Fatal("lock info not removed")
    90  	}
    91  }
    92  
    93  // Verify that we can write to the state file, as Windows' mandatory locking
    94  // will prevent writing to a handle different than the one that hold the lock.
    95  func TestLocalState_writeWhileLocked(t *testing.T) {
    96  	s := testLocalState(t)
    97  	defer os.Remove(s.Path)
    98  
    99  	// lock first
   100  	info := NewLockInfo()
   101  	info.Operation = "test"
   102  	lockID, err := s.Lock(info)
   103  	if err != nil {
   104  		t.Fatal(err)
   105  	}
   106  	defer func() {
   107  		if err := s.Unlock(lockID); err != nil {
   108  			t.Fatal(err)
   109  		}
   110  	}()
   111  
   112  	if err := s.WriteState(TestStateInitial()); err != nil {
   113  		t.Fatal(err)
   114  	}
   115  }
   116  
   117  func TestLocalState_pathOut(t *testing.T) {
   118  	f, err := ioutil.TempFile("", "tf")
   119  	if err != nil {
   120  		t.Fatalf("err: %s", err)
   121  	}
   122  	f.Close()
   123  	defer os.Remove(f.Name())
   124  
   125  	ls := testLocalState(t)
   126  	ls.PathOut = f.Name()
   127  	defer os.Remove(ls.Path)
   128  
   129  	TestState(t, ls)
   130  }
   131  
   132  func TestLocalState_nonExist(t *testing.T) {
   133  	ls := &LocalState{Path: "ishouldntexist"}
   134  	if err := ls.RefreshState(); err != nil {
   135  		t.Fatalf("err: %s", err)
   136  	}
   137  
   138  	if state := ls.State(); state != nil {
   139  		t.Fatalf("bad: %#v", state)
   140  	}
   141  }
   142  
   143  func TestLocalState_impl(t *testing.T) {
   144  	var _ StateReader = new(LocalState)
   145  	var _ StateWriter = new(LocalState)
   146  	var _ StatePersister = new(LocalState)
   147  	var _ StateRefresher = new(LocalState)
   148  }
   149  
   150  func testLocalState(t *testing.T) *LocalState {
   151  	f, err := ioutil.TempFile("", "tf")
   152  	if err != nil {
   153  		t.Fatalf("err: %s", err)
   154  	}
   155  
   156  	err = terraform.WriteState(TestStateInitial(), f)
   157  	f.Close()
   158  	if err != nil {
   159  		t.Fatalf("err: %s", err)
   160  	}
   161  
   162  	ls := &LocalState{Path: f.Name()}
   163  	if err := ls.RefreshState(); err != nil {
   164  		t.Fatalf("bad: %s", err)
   165  	}
   166  
   167  	return ls
   168  }
   169  
   170  // Make sure we can refresh while the state is locked
   171  func TestLocalState_refreshWhileLocked(t *testing.T) {
   172  	f, err := ioutil.TempFile("", "tf")
   173  	if err != nil {
   174  		t.Fatalf("err: %s", err)
   175  	}
   176  
   177  	err = terraform.WriteState(TestStateInitial(), f)
   178  	f.Close()
   179  	if err != nil {
   180  		t.Fatalf("err: %s", err)
   181  	}
   182  
   183  	s := &LocalState{Path: f.Name()}
   184  	defer os.Remove(s.Path)
   185  
   186  	// lock first
   187  	info := NewLockInfo()
   188  	info.Operation = "test"
   189  	lockID, err := s.Lock(info)
   190  	if err != nil {
   191  		t.Fatal(err)
   192  	}
   193  	defer func() {
   194  		if err := s.Unlock(lockID); err != nil {
   195  			t.Fatal(err)
   196  		}
   197  	}()
   198  
   199  	if err := s.RefreshState(); err != nil {
   200  		t.Fatal(err)
   201  	}
   202  
   203  	readState := s.State()
   204  	if readState == nil || readState.Lineage == "" {
   205  		t.Fatal("missing state")
   206  	}
   207  }