github.com/spirius/terraform@v0.10.0-beta2.0.20170714185654-87b2c0cf8fea/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 // Get the proper config structure 17 rc, err := config.NewRawConfig(c) 18 if err != nil { 19 t.Fatalf("bad: %s", err) 20 } 21 conf := terraform.NewResourceConfig(rc) 22 23 // Validate 24 warns, errs := b.Validate(conf) 25 if len(warns) > 0 { 26 t.Fatalf("warnings: %s", warns) 27 } 28 if len(errs) > 0 { 29 t.Fatalf("errors: %s", errs) 30 } 31 32 // Configure 33 if err := b.Configure(conf); err != nil { 34 t.Fatalf("err: %s", err) 35 } 36 37 return b 38 } 39 40 // TestBackend will test the functionality of a Backend. The backend is 41 // assumed to already be configured. This will test state functionality. 42 // If the backend reports it doesn't support multi-state by returning the 43 // error ErrNamedStatesNotSupported, then it will not test that. 44 // 45 // If you want to test locking, two backends must be given. If b2 is nil, 46 // then state lockign won't be tested. 47 func TestBackend(t *testing.T, b1, b2 Backend) { 48 testBackendStates(t, b1) 49 50 if b2 != nil { 51 testBackendStateLock(t, b1, b2) 52 } 53 } 54 55 func testBackendStates(t *testing.T, b Backend) { 56 states, err := b.States() 57 if err == ErrNamedStatesNotSupported { 58 t.Logf("TestBackend: named states not supported in %T, skipping", b) 59 return 60 } 61 62 // Test it starts with only the default 63 if len(states) != 1 || states[0] != DefaultStateName { 64 t.Fatalf("should only have default to start: %#v", states) 65 } 66 67 // Create a couple states 68 foo, err := b.State("foo") 69 if err != nil { 70 t.Fatalf("error: %s", err) 71 } 72 if err := foo.RefreshState(); err != nil { 73 t.Fatalf("bad: %s", err) 74 } 75 if v := foo.State(); v.HasResources() { 76 t.Fatalf("should be empty: %s", v) 77 } 78 79 bar, err := b.State("bar") 80 if err != nil { 81 t.Fatalf("error: %s", err) 82 } 83 if err := bar.RefreshState(); err != nil { 84 t.Fatalf("bad: %s", err) 85 } 86 if v := bar.State(); v.HasResources() { 87 t.Fatalf("should be empty: %s", v) 88 } 89 90 // Verify they are distinct states that can be read back from storage 91 { 92 // start with a fresh state, and record the lineage being 93 // written to "bar" 94 barState := terraform.NewState() 95 96 // creating the named state may have created a lineage, so use that if it exists. 97 if s := bar.State(); s != nil && s.Lineage != "" { 98 barState.Lineage = s.Lineage 99 } 100 barLineage := barState.Lineage 101 102 // the foo lineage should be distinct from bar, and unchanged after 103 // modifying bar 104 fooState := terraform.NewState() 105 // creating the named state may have created a lineage, so use that if it exists. 106 if s := foo.State(); s != nil && s.Lineage != "" { 107 fooState.Lineage = s.Lineage 108 } 109 fooLineage := fooState.Lineage 110 111 // write a known state to foo 112 if err := foo.WriteState(fooState); err != nil { 113 t.Fatal("error writing foo state:", err) 114 } 115 if err := foo.PersistState(); err != nil { 116 t.Fatal("error persisting foo state:", err) 117 } 118 119 // write a distinct known state to bar 120 if err := bar.WriteState(barState); err != nil { 121 t.Fatalf("bad: %s", err) 122 } 123 if err := bar.PersistState(); err != nil { 124 t.Fatalf("bad: %s", err) 125 } 126 127 // verify that foo is unchanged with the existing state manager 128 if err := foo.RefreshState(); err != nil { 129 t.Fatal("error refreshing foo:", err) 130 } 131 fooState = foo.State() 132 switch { 133 case fooState == nil: 134 t.Fatal("nil state read from foo") 135 case fooState.Lineage == barLineage: 136 t.Fatalf("bar lineage read from foo: %#v", fooState) 137 case fooState.Lineage != fooLineage: 138 t.Fatal("foo lineage alterred") 139 } 140 141 // fetch foo again from the backend 142 foo, err = b.State("foo") 143 if err != nil { 144 t.Fatal("error re-fetching state:", err) 145 } 146 if err := foo.RefreshState(); err != nil { 147 t.Fatal("error refreshing foo:", err) 148 } 149 fooState = foo.State() 150 switch { 151 case fooState == nil: 152 t.Fatal("nil state read from foo") 153 case fooState.Lineage != fooLineage: 154 t.Fatal("incorrect state returned from backend") 155 } 156 157 // fetch the bar again from the backend 158 bar, err = b.State("bar") 159 if err != nil { 160 t.Fatal("error re-fetching state:", err) 161 } 162 if err := bar.RefreshState(); err != nil { 163 t.Fatal("error refreshing bar:", err) 164 } 165 barState = bar.State() 166 switch { 167 case barState == nil: 168 t.Fatal("nil state read from bar") 169 case barState.Lineage != barLineage: 170 t.Fatal("incorrect state returned from backend") 171 } 172 } 173 174 // Verify we can now list them 175 { 176 // we determined that named stated are supported earlier 177 states, err := b.States() 178 if err != nil { 179 t.Fatal(err) 180 } 181 182 sort.Strings(states) 183 expected := []string{"bar", "default", "foo"} 184 if !reflect.DeepEqual(states, expected) { 185 t.Fatalf("bad: %#v", states) 186 } 187 } 188 189 // Delete some states 190 if err := b.DeleteState("foo"); err != nil { 191 t.Fatalf("err: %s", err) 192 } 193 194 // Verify the default state can't be deleted 195 if err := b.DeleteState(DefaultStateName); err == nil { 196 t.Fatal("expected error") 197 } 198 199 // Create and delete the foo state again. 200 // Make sure that there are no leftover artifacts from a deleted state 201 // preventing re-creation. 202 foo, err = b.State("foo") 203 if err != nil { 204 t.Fatalf("error: %s", err) 205 } 206 if err := foo.RefreshState(); err != nil { 207 t.Fatalf("bad: %s", err) 208 } 209 if v := foo.State(); v.HasResources() { 210 t.Fatalf("should be empty: %s", v) 211 } 212 // and delete it again 213 if err := b.DeleteState("foo"); err != nil { 214 t.Fatalf("err: %s", err) 215 } 216 217 // Verify deletion 218 { 219 states, err := b.States() 220 if err == ErrNamedStatesNotSupported { 221 t.Logf("TestBackend: named states not supported in %T, skipping", b) 222 return 223 } 224 225 sort.Strings(states) 226 expected := []string{"bar", "default"} 227 if !reflect.DeepEqual(states, expected) { 228 t.Fatalf("bad: %#v", states) 229 } 230 } 231 } 232 233 func testBackendStateLock(t *testing.T, b1, b2 Backend) { 234 // Get the default state for each 235 b1StateMgr, err := b1.State(DefaultStateName) 236 if err != nil { 237 t.Fatalf("error: %s", err) 238 } 239 if err := b1StateMgr.RefreshState(); err != nil { 240 t.Fatalf("bad: %s", err) 241 } 242 243 // Fast exit if this doesn't support locking at all 244 if _, ok := b1StateMgr.(state.Locker); !ok { 245 t.Logf("TestBackend: backend %T doesn't support state locking, not testing", b1) 246 return 247 } 248 249 t.Logf("TestBackend: testing state locking for %T", b1) 250 251 b2StateMgr, err := b2.State(DefaultStateName) 252 if err != nil { 253 t.Fatalf("error: %s", err) 254 } 255 if err := b2StateMgr.RefreshState(); err != nil { 256 t.Fatalf("bad: %s", err) 257 } 258 259 // Reassign so its obvious whats happening 260 lockerA := b1StateMgr.(state.Locker) 261 lockerB := b2StateMgr.(state.Locker) 262 263 infoA := state.NewLockInfo() 264 infoA.Operation = "test" 265 infoA.Who = "clientA" 266 267 infoB := state.NewLockInfo() 268 infoB.Operation = "test" 269 infoB.Who = "clientB" 270 271 lockIDA, err := lockerA.Lock(infoA) 272 if err != nil { 273 t.Fatal("unable to get initial lock:", err) 274 } 275 276 // If the lock ID is blank, assume locking is disabled 277 if lockIDA == "" { 278 t.Logf("TestBackend: %T: empty string returned for lock, assuming disabled", b1) 279 return 280 } 281 282 _, err = lockerB.Lock(infoB) 283 if err == nil { 284 lockerA.Unlock(lockIDA) 285 t.Fatal("client B obtained lock while held by client A") 286 } 287 288 if err := lockerA.Unlock(lockIDA); err != nil { 289 t.Fatal("error unlocking client A", err) 290 } 291 292 lockIDB, err := lockerB.Lock(infoB) 293 if err != nil { 294 t.Fatal("unable to obtain lock from client B") 295 } 296 297 if lockIDB == lockIDA { 298 t.Fatalf("duplicate lock IDs: %q", lockIDB) 299 } 300 301 if err = lockerB.Unlock(lockIDB); err != nil { 302 t.Fatal("error unlocking client B:", err) 303 } 304 305 }