github.com/lymingtonprecision/terraform@v0.9.9-0.20170613092852-62acef9611a9/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 barLineage := barState.Lineage 96 97 // the foo lineage should be distinct from bar, and unchanged after 98 // modifying bar 99 fooState := terraform.NewState() 100 fooLineage := fooState.Lineage 101 102 // write a known state to foo 103 if err := foo.WriteState(fooState); err != nil { 104 t.Fatal("error writing foo state:", err) 105 } 106 if err := foo.PersistState(); err != nil { 107 t.Fatal("error persisting foo state:", err) 108 } 109 110 // write a distinct known state to bar 111 if err := bar.WriteState(barState); err != nil { 112 t.Fatalf("bad: %s", err) 113 } 114 if err := bar.PersistState(); err != nil { 115 t.Fatalf("bad: %s", err) 116 } 117 118 // verify that foo is unchanged with the existing state manager 119 if err := foo.RefreshState(); err != nil { 120 t.Fatal("error refreshing foo:", err) 121 } 122 fooState = foo.State() 123 switch { 124 case fooState == nil: 125 t.Fatal("nil state read from foo") 126 case fooState.Lineage == barLineage: 127 t.Fatalf("bar lineage read from foo: %#v", fooState) 128 case fooState.Lineage != fooLineage: 129 t.Fatal("foo lineage alterred") 130 } 131 132 // fetch foo again from the backend 133 foo, err = b.State("foo") 134 if err != nil { 135 t.Fatal("error re-fetching state:", err) 136 } 137 if err := foo.RefreshState(); err != nil { 138 t.Fatal("error refreshing foo:", err) 139 } 140 fooState = foo.State() 141 switch { 142 case fooState == nil: 143 t.Fatal("nil state read from foo") 144 case fooState.Lineage != fooLineage: 145 t.Fatal("incorrect state returned from backend") 146 } 147 148 // fetch the bar again from the backend 149 bar, err = b.State("bar") 150 if err != nil { 151 t.Fatal("error re-fetching state:", err) 152 } 153 if err := bar.RefreshState(); err != nil { 154 t.Fatal("error refreshing bar:", err) 155 } 156 barState = bar.State() 157 switch { 158 case barState == nil: 159 t.Fatal("nil state read from bar") 160 case barState.Lineage != barLineage: 161 t.Fatal("incorrect state returned from backend") 162 } 163 } 164 165 // Verify we can now list them 166 { 167 // we determined that named stated are supported earlier 168 states, err := b.States() 169 if err != nil { 170 t.Fatal(err) 171 } 172 173 sort.Strings(states) 174 expected := []string{"bar", "default", "foo"} 175 if !reflect.DeepEqual(states, expected) { 176 t.Fatalf("bad: %#v", states) 177 } 178 } 179 180 // Delete some states 181 if err := b.DeleteState("foo"); err != nil { 182 t.Fatalf("err: %s", err) 183 } 184 185 // Verify the default state can't be deleted 186 if err := b.DeleteState(DefaultStateName); err == nil { 187 t.Fatal("expected error") 188 } 189 190 // Verify deletion 191 { 192 states, err := b.States() 193 if err == ErrNamedStatesNotSupported { 194 t.Logf("TestBackend: named states not supported in %T, skipping", b) 195 return 196 } 197 198 sort.Strings(states) 199 expected := []string{"bar", "default"} 200 if !reflect.DeepEqual(states, expected) { 201 t.Fatalf("bad: %#v", states) 202 } 203 } 204 } 205 206 func testBackendStateLock(t *testing.T, b1, b2 Backend) { 207 // Get the default state for each 208 b1StateMgr, err := b1.State(DefaultStateName) 209 if err != nil { 210 t.Fatalf("error: %s", err) 211 } 212 if err := b1StateMgr.RefreshState(); err != nil { 213 t.Fatalf("bad: %s", err) 214 } 215 216 // Fast exit if this doesn't support locking at all 217 if _, ok := b1StateMgr.(state.Locker); !ok { 218 t.Logf("TestBackend: backend %T doesn't support state locking, not testing", b1) 219 return 220 } 221 222 t.Logf("TestBackend: testing state locking for %T", b1) 223 224 b2StateMgr, err := b2.State(DefaultStateName) 225 if err != nil { 226 t.Fatalf("error: %s", err) 227 } 228 if err := b2StateMgr.RefreshState(); err != nil { 229 t.Fatalf("bad: %s", err) 230 } 231 232 // Reassign so its obvious whats happening 233 lockerA := b1StateMgr.(state.Locker) 234 lockerB := b2StateMgr.(state.Locker) 235 236 infoA := state.NewLockInfo() 237 infoA.Operation = "test" 238 infoA.Who = "clientA" 239 240 infoB := state.NewLockInfo() 241 infoB.Operation = "test" 242 infoB.Who = "clientB" 243 244 lockIDA, err := lockerA.Lock(infoA) 245 if err != nil { 246 t.Fatal("unable to get initial lock:", err) 247 } 248 249 // If the lock ID is blank, assume locking is disabled 250 if lockIDA == "" { 251 t.Logf("TestBackend: %T: empty string returned for lock, assuming disabled", b1) 252 return 253 } 254 255 _, err = lockerB.Lock(infoB) 256 if err == nil { 257 lockerA.Unlock(lockIDA) 258 t.Fatal("client B obtained lock while held by client A") 259 } 260 261 if err := lockerA.Unlock(lockIDA); err != nil { 262 t.Fatal("error unlocking client A", err) 263 } 264 265 lockIDB, err := lockerB.Lock(infoB) 266 if err != nil { 267 t.Fatal("unable to obtain lock from client B") 268 } 269 270 if lockIDB == lockIDA { 271 t.Fatalf("duplicate lock IDs: %q", lockIDB) 272 } 273 274 if err = lockerB.Unlock(lockIDB); err != nil { 275 t.Fatal("error unlocking client B:", err) 276 } 277 278 }