github.com/Kevinklinger/open_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 }