github.com/jzbruno/terraform@v0.10.3-0.20180104230435-18975d727047/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 }