github.com/r3vit/terraform@v0.11.9-beta1.0.20181016131357-87d05607d3c5/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 }