github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/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 }