github.com/acm1/terraform@v0.6.2-0.20150729164239-1f314444f45c/command/push_test.go (about) 1 package command 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "compress/gzip" 7 "io" 8 "os" 9 "reflect" 10 "sort" 11 "testing" 12 13 "github.com/hashicorp/terraform/terraform" 14 "github.com/mitchellh/cli" 15 ) 16 17 func TestPush_good(t *testing.T) { 18 tmp, cwd := testCwd(t) 19 defer testFixCwd(t, tmp, cwd) 20 21 // Create remote state file, this should be pulled 22 conf, srv := testRemoteState(t, testState(), 200) 23 defer srv.Close() 24 25 // Persist local remote state 26 s := terraform.NewState() 27 s.Serial = 5 28 s.Remote = conf 29 testStateFileRemote(t, s) 30 31 // Path where the archive will be "uploaded" to 32 archivePath := testTempFile(t) 33 defer os.Remove(archivePath) 34 35 client := &mockPushClient{File: archivePath} 36 ui := new(cli.MockUi) 37 c := &PushCommand{ 38 Meta: Meta{ 39 ContextOpts: testCtxConfig(testProvider()), 40 Ui: ui, 41 }, 42 43 client: client, 44 } 45 46 args := []string{ 47 "-vcs=false", 48 testFixturePath("push"), 49 } 50 if code := c.Run(args); code != 0 { 51 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 52 } 53 54 actual := testArchiveStr(t, archivePath) 55 expected := []string{ 56 ".terraform/", 57 ".terraform/terraform.tfstate", 58 "main.tf", 59 } 60 if !reflect.DeepEqual(actual, expected) { 61 t.Fatalf("bad: %#v", actual) 62 } 63 64 variables := make(map[string]string) 65 if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { 66 t.Fatalf("bad: %#v", client.UpsertOptions) 67 } 68 69 if client.UpsertOptions.Name != "foo" { 70 t.Fatalf("bad: %#v", client.UpsertOptions) 71 } 72 } 73 74 func TestPush_input(t *testing.T) { 75 tmp, cwd := testCwd(t) 76 defer testFixCwd(t, tmp, cwd) 77 78 // Create remote state file, this should be pulled 79 conf, srv := testRemoteState(t, testState(), 200) 80 defer srv.Close() 81 82 // Persist local remote state 83 s := terraform.NewState() 84 s.Serial = 5 85 s.Remote = conf 86 testStateFileRemote(t, s) 87 88 // Path where the archive will be "uploaded" to 89 archivePath := testTempFile(t) 90 defer os.Remove(archivePath) 91 92 client := &mockPushClient{File: archivePath} 93 ui := new(cli.MockUi) 94 c := &PushCommand{ 95 Meta: Meta{ 96 ContextOpts: testCtxConfig(testProvider()), 97 Ui: ui, 98 }, 99 100 client: client, 101 } 102 103 // Disable test mode so input would be asked and setup the 104 // input reader/writers. 105 test = false 106 defer func() { test = true }() 107 defaultInputReader = bytes.NewBufferString("foo\n") 108 defaultInputWriter = new(bytes.Buffer) 109 110 args := []string{ 111 "-vcs=false", 112 testFixturePath("push-input"), 113 } 114 if code := c.Run(args); code != 0 { 115 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 116 } 117 118 variables := map[string]string{ 119 "foo": "foo", 120 } 121 if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { 122 t.Fatalf("bad: %#v", client.UpsertOptions.Variables) 123 } 124 } 125 126 func TestPush_inputPartial(t *testing.T) { 127 tmp, cwd := testCwd(t) 128 defer testFixCwd(t, tmp, cwd) 129 130 // Create remote state file, this should be pulled 131 conf, srv := testRemoteState(t, testState(), 200) 132 defer srv.Close() 133 134 // Persist local remote state 135 s := terraform.NewState() 136 s.Serial = 5 137 s.Remote = conf 138 testStateFileRemote(t, s) 139 140 // Path where the archive will be "uploaded" to 141 archivePath := testTempFile(t) 142 defer os.Remove(archivePath) 143 144 client := &mockPushClient{ 145 File: archivePath, 146 GetResult: map[string]string{"foo": "bar"}, 147 } 148 ui := new(cli.MockUi) 149 c := &PushCommand{ 150 Meta: Meta{ 151 ContextOpts: testCtxConfig(testProvider()), 152 Ui: ui, 153 }, 154 155 client: client, 156 } 157 158 // Disable test mode so input would be asked and setup the 159 // input reader/writers. 160 test = false 161 defer func() { test = true }() 162 defaultInputReader = bytes.NewBufferString("foo\n") 163 defaultInputWriter = new(bytes.Buffer) 164 165 args := []string{ 166 "-vcs=false", 167 testFixturePath("push-input-partial"), 168 } 169 if code := c.Run(args); code != 0 { 170 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 171 } 172 173 variables := map[string]string{ 174 "foo": "bar", 175 "bar": "foo", 176 } 177 if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { 178 t.Fatalf("bad: %#v", client.UpsertOptions) 179 } 180 } 181 182 // This tests that the push command will override Atlas variables 183 // if requested. 184 func TestPush_localOverride(t *testing.T) { 185 // Disable test mode so input would be asked and setup the 186 // input reader/writers. 187 test = false 188 defer func() { test = true }() 189 defaultInputReader = bytes.NewBufferString("nope\n") 190 defaultInputWriter = new(bytes.Buffer) 191 192 tmp, cwd := testCwd(t) 193 defer testFixCwd(t, tmp, cwd) 194 195 // Create remote state file, this should be pulled 196 conf, srv := testRemoteState(t, testState(), 200) 197 defer srv.Close() 198 199 // Persist local remote state 200 s := terraform.NewState() 201 s.Serial = 5 202 s.Remote = conf 203 testStateFileRemote(t, s) 204 205 // Path where the archive will be "uploaded" to 206 archivePath := testTempFile(t) 207 defer os.Remove(archivePath) 208 209 client := &mockPushClient{File: archivePath} 210 // Provided vars should override existing ones 211 client.GetResult = map[string]string{ 212 "foo": "old", 213 } 214 ui := new(cli.MockUi) 215 c := &PushCommand{ 216 Meta: Meta{ 217 ContextOpts: testCtxConfig(testProvider()), 218 Ui: ui, 219 }, 220 221 client: client, 222 } 223 224 path := testFixturePath("push-tfvars") 225 args := []string{ 226 "-var-file", path + "/terraform.tfvars", 227 "-vcs=false", 228 "-overwrite=foo", 229 path, 230 } 231 if code := c.Run(args); code != 0 { 232 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 233 } 234 235 actual := testArchiveStr(t, archivePath) 236 expected := []string{ 237 ".terraform/", 238 ".terraform/terraform.tfstate", 239 "main.tf", 240 "terraform.tfvars", 241 } 242 if !reflect.DeepEqual(actual, expected) { 243 t.Fatalf("bad: %#v", actual) 244 } 245 246 if client.UpsertOptions.Name != "foo" { 247 t.Fatalf("bad: %#v", client.UpsertOptions) 248 } 249 250 variables := map[string]string{ 251 "foo": "bar", 252 "bar": "foo", 253 } 254 if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { 255 t.Fatalf("bad: %#v", client.UpsertOptions) 256 } 257 } 258 259 // This tests that the push command prefers Atlas variables over 260 // local ones. 261 func TestPush_preferAtlas(t *testing.T) { 262 // Disable test mode so input would be asked and setup the 263 // input reader/writers. 264 test = false 265 defer func() { test = true }() 266 defaultInputReader = bytes.NewBufferString("nope\n") 267 defaultInputWriter = new(bytes.Buffer) 268 269 tmp, cwd := testCwd(t) 270 defer testFixCwd(t, tmp, cwd) 271 272 // Create remote state file, this should be pulled 273 conf, srv := testRemoteState(t, testState(), 200) 274 defer srv.Close() 275 276 // Persist local remote state 277 s := terraform.NewState() 278 s.Serial = 5 279 s.Remote = conf 280 testStateFileRemote(t, s) 281 282 // Path where the archive will be "uploaded" to 283 archivePath := testTempFile(t) 284 defer os.Remove(archivePath) 285 286 client := &mockPushClient{File: archivePath} 287 // Provided vars should override existing ones 288 client.GetResult = map[string]string{ 289 "foo": "old", 290 } 291 ui := new(cli.MockUi) 292 c := &PushCommand{ 293 Meta: Meta{ 294 ContextOpts: testCtxConfig(testProvider()), 295 Ui: ui, 296 }, 297 298 client: client, 299 } 300 301 path := testFixturePath("push-tfvars") 302 args := []string{ 303 "-var-file", path + "/terraform.tfvars", 304 "-vcs=false", 305 path, 306 } 307 if code := c.Run(args); code != 0 { 308 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 309 } 310 311 actual := testArchiveStr(t, archivePath) 312 expected := []string{ 313 ".terraform/", 314 ".terraform/terraform.tfstate", 315 "main.tf", 316 "terraform.tfvars", 317 } 318 if !reflect.DeepEqual(actual, expected) { 319 t.Fatalf("bad: %#v", actual) 320 } 321 322 if client.UpsertOptions.Name != "foo" { 323 t.Fatalf("bad: %#v", client.UpsertOptions) 324 } 325 326 variables := map[string]string{ 327 "foo": "old", 328 "bar": "foo", 329 } 330 if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { 331 t.Fatalf("bad: %#v", client.UpsertOptions) 332 } 333 } 334 335 // This tests that the push command will send the variables in tfvars 336 func TestPush_tfvars(t *testing.T) { 337 // Disable test mode so input would be asked and setup the 338 // input reader/writers. 339 test = false 340 defer func() { test = true }() 341 defaultInputReader = bytes.NewBufferString("nope\n") 342 defaultInputWriter = new(bytes.Buffer) 343 344 tmp, cwd := testCwd(t) 345 defer testFixCwd(t, tmp, cwd) 346 347 // Create remote state file, this should be pulled 348 conf, srv := testRemoteState(t, testState(), 200) 349 defer srv.Close() 350 351 // Persist local remote state 352 s := terraform.NewState() 353 s.Serial = 5 354 s.Remote = conf 355 testStateFileRemote(t, s) 356 357 // Path where the archive will be "uploaded" to 358 archivePath := testTempFile(t) 359 defer os.Remove(archivePath) 360 361 client := &mockPushClient{File: archivePath} 362 ui := new(cli.MockUi) 363 c := &PushCommand{ 364 Meta: Meta{ 365 ContextOpts: testCtxConfig(testProvider()), 366 Ui: ui, 367 }, 368 369 client: client, 370 } 371 372 path := testFixturePath("push-tfvars") 373 args := []string{ 374 "-var-file", path + "/terraform.tfvars", 375 "-vcs=false", 376 path, 377 } 378 if code := c.Run(args); code != 0 { 379 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 380 } 381 382 actual := testArchiveStr(t, archivePath) 383 expected := []string{ 384 ".terraform/", 385 ".terraform/terraform.tfstate", 386 "main.tf", 387 "terraform.tfvars", 388 } 389 if !reflect.DeepEqual(actual, expected) { 390 t.Fatalf("bad: %#v", actual) 391 } 392 393 if client.UpsertOptions.Name != "foo" { 394 t.Fatalf("bad: %#v", client.UpsertOptions) 395 } 396 397 variables := map[string]string{ 398 "foo": "bar", 399 "bar": "foo", 400 } 401 if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { 402 t.Fatalf("bad: %#v", client.UpsertOptions) 403 } 404 } 405 406 func TestPush_name(t *testing.T) { 407 tmp, cwd := testCwd(t) 408 defer testFixCwd(t, tmp, cwd) 409 410 // Create remote state file, this should be pulled 411 conf, srv := testRemoteState(t, testState(), 200) 412 defer srv.Close() 413 414 // Persist local remote state 415 s := terraform.NewState() 416 s.Serial = 5 417 s.Remote = conf 418 testStateFileRemote(t, s) 419 420 // Path where the archive will be "uploaded" to 421 archivePath := testTempFile(t) 422 defer os.Remove(archivePath) 423 424 client := &mockPushClient{File: archivePath} 425 ui := new(cli.MockUi) 426 c := &PushCommand{ 427 Meta: Meta{ 428 ContextOpts: testCtxConfig(testProvider()), 429 Ui: ui, 430 }, 431 432 client: client, 433 } 434 435 args := []string{ 436 "-name", "bar", 437 "-vcs=false", 438 testFixturePath("push"), 439 } 440 if code := c.Run(args); code != 0 { 441 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 442 } 443 444 if client.UpsertOptions.Name != "bar" { 445 t.Fatalf("bad: %#v", client.UpsertOptions) 446 } 447 } 448 449 func TestPush_noState(t *testing.T) { 450 tmp, cwd := testCwd(t) 451 defer testFixCwd(t, tmp, cwd) 452 453 ui := new(cli.MockUi) 454 c := &PushCommand{ 455 Meta: Meta{ 456 ContextOpts: testCtxConfig(testProvider()), 457 Ui: ui, 458 }, 459 } 460 461 args := []string{} 462 if code := c.Run(args); code != 1 { 463 t.Fatalf("bad: \n%s", ui.ErrorWriter.String()) 464 } 465 } 466 467 func TestPush_noRemoteState(t *testing.T) { 468 state := &terraform.State{ 469 Modules: []*terraform.ModuleState{ 470 &terraform.ModuleState{ 471 Path: []string{"root"}, 472 Resources: map[string]*terraform.ResourceState{ 473 "test_instance.foo": &terraform.ResourceState{ 474 Type: "test_instance", 475 Primary: &terraform.InstanceState{ 476 ID: "bar", 477 }, 478 }, 479 }, 480 }, 481 }, 482 } 483 statePath := testStateFile(t, state) 484 485 ui := new(cli.MockUi) 486 c := &PushCommand{ 487 Meta: Meta{ 488 Ui: ui, 489 }, 490 } 491 492 args := []string{ 493 "-state", statePath, 494 } 495 if code := c.Run(args); code != 1 { 496 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 497 } 498 } 499 500 func TestPush_plan(t *testing.T) { 501 tmp, cwd := testCwd(t) 502 defer testFixCwd(t, tmp, cwd) 503 504 // Create remote state file, this should be pulled 505 conf, srv := testRemoteState(t, testState(), 200) 506 defer srv.Close() 507 508 // Persist local remote state 509 s := terraform.NewState() 510 s.Serial = 5 511 s.Remote = conf 512 testStateFileRemote(t, s) 513 514 // Create a plan 515 planPath := testPlanFile(t, &terraform.Plan{ 516 Module: testModule(t, "apply"), 517 }) 518 519 ui := new(cli.MockUi) 520 c := &PushCommand{ 521 Meta: Meta{ 522 ContextOpts: testCtxConfig(testProvider()), 523 Ui: ui, 524 }, 525 } 526 527 args := []string{planPath} 528 if code := c.Run(args); code != 1 { 529 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 530 } 531 } 532 533 func testArchiveStr(t *testing.T, path string) []string { 534 f, err := os.Open(path) 535 if err != nil { 536 t.Fatalf("err: %s", err) 537 } 538 defer f.Close() 539 540 // Ungzip 541 gzipR, err := gzip.NewReader(f) 542 if err != nil { 543 t.Fatalf("err: %s", err) 544 } 545 546 // Accumulator 547 result := make([]string, 0, 10) 548 549 // Untar 550 tarR := tar.NewReader(gzipR) 551 for { 552 header, err := tarR.Next() 553 if err == io.EOF { 554 break 555 } 556 if err != nil { 557 t.Fatalf("err: %s", err) 558 } 559 560 result = append(result, header.Name) 561 } 562 563 sort.Strings(result) 564 return result 565 }