github.com/jgadling/terraform@v0.3.8-0.20150227214559-abd68c2c87bc/command/remote_test.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/hashicorp/terraform/state" 11 "github.com/hashicorp/terraform/terraform" 12 "github.com/mitchellh/cli" 13 ) 14 15 // Test disabling remote management 16 func TestRemote_disable(t *testing.T) { 17 tmp, cwd := testCwd(t) 18 defer testFixCwd(t, tmp, cwd) 19 20 // Create remote state file, this should be pulled 21 s := terraform.NewState() 22 s.Serial = 10 23 conf, srv := testRemoteState(t, s, 200) 24 defer srv.Close() 25 26 // Persist local remote state 27 s = terraform.NewState() 28 s.Serial = 5 29 s.Remote = conf 30 31 // Write the state 32 statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) 33 state := &state.LocalState{Path: statePath} 34 if err := state.WriteState(s); err != nil { 35 t.Fatalf("err: %s", err) 36 } 37 if err := state.PersistState(); err != nil { 38 t.Fatalf("err: %s", err) 39 } 40 41 ui := new(cli.MockUi) 42 c := &RemoteCommand{ 43 Meta: Meta{ 44 ContextOpts: testCtxConfig(testProvider()), 45 Ui: ui, 46 }, 47 } 48 args := []string{"-disable"} 49 if code := c.Run(args); code != 0 { 50 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 51 } 52 53 // Local state file should be removed and the local cache should exist 54 testRemoteLocal(t, true) 55 testRemoteLocalCache(t, false) 56 57 // Check that the state file was updated 58 raw, _ := ioutil.ReadFile(DefaultStateFilename) 59 newState, err := terraform.ReadState(bytes.NewReader(raw)) 60 if err != nil { 61 t.Fatalf("err: %v", err) 62 } 63 64 // Ensure we updated 65 if newState.Remote != nil { 66 t.Fatalf("remote configuration not removed") 67 } 68 } 69 70 // Test disabling remote management without pulling 71 func TestRemote_disable_noPull(t *testing.T) { 72 tmp, cwd := testCwd(t) 73 defer testFixCwd(t, tmp, cwd) 74 75 // Create remote state file, this should be pulled 76 s := terraform.NewState() 77 s.Serial = 10 78 conf, srv := testRemoteState(t, s, 200) 79 defer srv.Close() 80 81 // Persist local remote state 82 s = terraform.NewState() 83 s.Serial = 5 84 s.Remote = conf 85 86 // Write the state 87 statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) 88 state := &state.LocalState{Path: statePath} 89 if err := state.WriteState(s); err != nil { 90 t.Fatalf("err: %s", err) 91 } 92 if err := state.PersistState(); err != nil { 93 t.Fatalf("err: %s", err) 94 } 95 96 ui := new(cli.MockUi) 97 c := &RemoteCommand{ 98 Meta: Meta{ 99 ContextOpts: testCtxConfig(testProvider()), 100 Ui: ui, 101 }, 102 } 103 args := []string{"-disable", "-pull=false"} 104 if code := c.Run(args); code != 0 { 105 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 106 } 107 108 // Local state file should be removed and the local cache should exist 109 testRemoteLocal(t, true) 110 testRemoteLocalCache(t, false) 111 112 // Check that the state file was updated 113 raw, _ := ioutil.ReadFile(DefaultStateFilename) 114 newState, err := terraform.ReadState(bytes.NewReader(raw)) 115 if err != nil { 116 t.Fatalf("err: %v", err) 117 } 118 119 if newState.Remote != nil { 120 t.Fatalf("remote configuration not removed") 121 } 122 } 123 124 // Test disabling remote management when not enabled 125 func TestRemote_disable_notEnabled(t *testing.T) { 126 tmp, cwd := testCwd(t) 127 defer testFixCwd(t, tmp, cwd) 128 129 ui := new(cli.MockUi) 130 c := &RemoteCommand{ 131 Meta: Meta{ 132 ContextOpts: testCtxConfig(testProvider()), 133 Ui: ui, 134 }, 135 } 136 137 args := []string{"-disable"} 138 if code := c.Run(args); code != 1 { 139 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 140 } 141 } 142 143 // Test disabling remote management with a state file in the way 144 func TestRemote_disable_otherState(t *testing.T) { 145 tmp, cwd := testCwd(t) 146 defer testFixCwd(t, tmp, cwd) 147 148 // Persist local remote state 149 s := terraform.NewState() 150 s.Serial = 5 151 152 // Write the state 153 statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) 154 state := &state.LocalState{Path: statePath} 155 if err := state.WriteState(s); err != nil { 156 t.Fatalf("err: %s", err) 157 } 158 if err := state.PersistState(); err != nil { 159 t.Fatalf("err: %s", err) 160 } 161 162 // Also put a file at the default path 163 fh, err := os.Create(DefaultStateFilename) 164 if err != nil { 165 t.Fatalf("err: %v", err) 166 } 167 err = terraform.WriteState(s, fh) 168 fh.Close() 169 if err != nil { 170 t.Fatalf("err: %v", err) 171 } 172 173 ui := new(cli.MockUi) 174 c := &RemoteCommand{ 175 Meta: Meta{ 176 ContextOpts: testCtxConfig(testProvider()), 177 Ui: ui, 178 }, 179 } 180 181 args := []string{"-disable"} 182 if code := c.Run(args); code != 1 { 183 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 184 } 185 } 186 187 // Test the case where both managed and non managed state present 188 func TestRemote_managedAndNonManaged(t *testing.T) { 189 tmp, cwd := testCwd(t) 190 defer testFixCwd(t, tmp, cwd) 191 192 // Persist local remote state 193 s := terraform.NewState() 194 s.Serial = 5 195 196 // Write the state 197 statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) 198 state := &state.LocalState{Path: statePath} 199 if err := state.WriteState(s); err != nil { 200 t.Fatalf("err: %s", err) 201 } 202 if err := state.PersistState(); err != nil { 203 t.Fatalf("err: %s", err) 204 } 205 206 // Also put a file at the default path 207 fh, err := os.Create(DefaultStateFilename) 208 if err != nil { 209 t.Fatalf("err: %v", err) 210 } 211 err = terraform.WriteState(s, fh) 212 fh.Close() 213 if err != nil { 214 t.Fatalf("err: %v", err) 215 } 216 217 ui := new(cli.MockUi) 218 c := &RemoteCommand{ 219 Meta: Meta{ 220 ContextOpts: testCtxConfig(testProvider()), 221 Ui: ui, 222 }, 223 } 224 225 args := []string{} 226 if code := c.Run(args); code != 1 { 227 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 228 } 229 } 230 231 // Test initializing blank state 232 func TestRemote_initBlank(t *testing.T) { 233 tmp, cwd := testCwd(t) 234 defer testFixCwd(t, tmp, cwd) 235 236 ui := new(cli.MockUi) 237 c := &RemoteCommand{ 238 Meta: Meta{ 239 ContextOpts: testCtxConfig(testProvider()), 240 Ui: ui, 241 }, 242 } 243 244 args := []string{ 245 "-backend=http", 246 "-backend-config", "address=http://example.com", 247 "-backend-config", "access_token=test", 248 } 249 if code := c.Run(args); code != 0 { 250 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 251 } 252 253 remotePath := filepath.Join(DefaultDataDir, DefaultStateFilename) 254 ls := &state.LocalState{Path: remotePath} 255 if err := ls.RefreshState(); err != nil { 256 t.Fatalf("err: %s", err) 257 } 258 259 local := ls.State() 260 if local.Remote.Type != "http" { 261 t.Fatalf("Bad: %#v", local.Remote) 262 } 263 if local.Remote.Config["address"] != "http://example.com" { 264 t.Fatalf("Bad: %#v", local.Remote) 265 } 266 if local.Remote.Config["access_token"] != "test" { 267 t.Fatalf("Bad: %#v", local.Remote) 268 } 269 } 270 271 // Test initializing without remote settings 272 func TestRemote_initBlank_missingRemote(t *testing.T) { 273 tmp, cwd := testCwd(t) 274 defer testFixCwd(t, tmp, cwd) 275 276 ui := new(cli.MockUi) 277 c := &RemoteCommand{ 278 Meta: Meta{ 279 ContextOpts: testCtxConfig(testProvider()), 280 Ui: ui, 281 }, 282 } 283 284 args := []string{} 285 if code := c.Run(args); code != 1 { 286 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 287 } 288 } 289 290 // Test updating remote config 291 func TestRemote_updateRemote(t *testing.T) { 292 tmp, cwd := testCwd(t) 293 defer testFixCwd(t, tmp, cwd) 294 295 // Persist local remote state 296 s := terraform.NewState() 297 s.Serial = 5 298 s.Remote = &terraform.RemoteState{ 299 Type: "invalid", 300 } 301 302 // Write the state 303 statePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename) 304 ls := &state.LocalState{Path: statePath} 305 if err := ls.WriteState(s); err != nil { 306 t.Fatalf("err: %s", err) 307 } 308 if err := ls.PersistState(); err != nil { 309 t.Fatalf("err: %s", err) 310 } 311 312 ui := new(cli.MockUi) 313 c := &RemoteCommand{ 314 Meta: Meta{ 315 ContextOpts: testCtxConfig(testProvider()), 316 Ui: ui, 317 }, 318 } 319 320 args := []string{ 321 "-backend=http", 322 "-backend-config", "address=http://example.com", 323 "-backend-config", "access_token=test", 324 } 325 if code := c.Run(args); code != 0 { 326 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 327 } 328 329 remotePath := filepath.Join(DefaultDataDir, DefaultStateFilename) 330 ls = &state.LocalState{Path: remotePath} 331 if err := ls.RefreshState(); err != nil { 332 t.Fatalf("err: %s", err) 333 } 334 local := ls.State() 335 336 if local.Remote.Type != "http" { 337 t.Fatalf("Bad: %#v", local.Remote) 338 } 339 if local.Remote.Config["address"] != "http://example.com" { 340 t.Fatalf("Bad: %#v", local.Remote) 341 } 342 if local.Remote.Config["access_token"] != "test" { 343 t.Fatalf("Bad: %#v", local.Remote) 344 } 345 } 346 347 // Test enabling remote state 348 func TestRemote_enableRemote(t *testing.T) { 349 tmp, cwd := testCwd(t) 350 defer testFixCwd(t, tmp, cwd) 351 352 // Create a non-remote enabled state 353 s := terraform.NewState() 354 s.Serial = 5 355 356 // Add the state at the default path 357 fh, err := os.Create(DefaultStateFilename) 358 if err != nil { 359 t.Fatalf("err: %v", err) 360 } 361 err = terraform.WriteState(s, fh) 362 fh.Close() 363 if err != nil { 364 t.Fatalf("err: %v", err) 365 } 366 367 ui := new(cli.MockUi) 368 c := &RemoteCommand{ 369 Meta: Meta{ 370 ContextOpts: testCtxConfig(testProvider()), 371 Ui: ui, 372 }, 373 } 374 375 args := []string{ 376 "-backend=http", 377 "-backend-config", "address=http://example.com", 378 "-backend-config", "access_token=test", 379 } 380 if code := c.Run(args); code != 0 { 381 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 382 } 383 384 remotePath := filepath.Join(DefaultDataDir, DefaultStateFilename) 385 ls := &state.LocalState{Path: remotePath} 386 if err := ls.RefreshState(); err != nil { 387 t.Fatalf("err: %s", err) 388 } 389 local := ls.State() 390 391 if local.Remote.Type != "http" { 392 t.Fatalf("Bad: %#v", local.Remote) 393 } 394 if local.Remote.Config["address"] != "http://example.com" { 395 t.Fatalf("Bad: %#v", local.Remote) 396 } 397 if local.Remote.Config["access_token"] != "test" { 398 t.Fatalf("Bad: %#v", local.Remote) 399 } 400 401 // Backup file should exist, state file should not 402 testRemoteLocal(t, false) 403 testRemoteLocalBackup(t, true) 404 } 405 406 func testRemoteLocal(t *testing.T, exists bool) { 407 _, err := os.Stat(DefaultStateFilename) 408 if os.IsNotExist(err) && !exists { 409 return 410 } 411 if err == nil && exists { 412 return 413 } 414 415 t.Fatalf("bad: %#v", err) 416 } 417 418 func testRemoteLocalBackup(t *testing.T, exists bool) { 419 _, err := os.Stat(DefaultStateFilename + DefaultBackupExtention) 420 if os.IsNotExist(err) && !exists { 421 return 422 } 423 if err == nil && exists { 424 return 425 } 426 if err == nil && !exists { 427 t.Fatal("expected local backup to exist") 428 } 429 430 t.Fatalf("bad: %#v", err) 431 } 432 433 func testRemoteLocalCache(t *testing.T, exists bool) { 434 _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)) 435 if os.IsNotExist(err) && !exists { 436 return 437 } 438 if err == nil && exists { 439 return 440 } 441 if err == nil && !exists { 442 t.Fatal("expected local cache to exist") 443 } 444 445 t.Fatalf("bad: %#v", err) 446 }