github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/command/import_test.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "strings" 8 "testing" 9 10 "github.com/hashicorp/terraform/helper/copy" 11 "github.com/hashicorp/terraform/plugin" 12 "github.com/hashicorp/terraform/plugin/discovery" 13 "github.com/hashicorp/terraform/terraform" 14 "github.com/mitchellh/cli" 15 ) 16 17 func TestImport(t *testing.T) { 18 defer testChdir(t, testFixturePath("import-provider-implicit"))() 19 20 statePath := testTempFile(t) 21 22 p := testProvider() 23 ui := new(cli.MockUi) 24 c := &ImportCommand{ 25 Meta: Meta{ 26 testingOverrides: metaOverridesForProvider(p), 27 Ui: ui, 28 }, 29 } 30 31 p.ImportStateFn = nil 32 p.ImportStateReturn = []*terraform.InstanceState{ 33 &terraform.InstanceState{ 34 ID: "yay", 35 Ephemeral: terraform.EphemeralState{ 36 Type: "test_instance", 37 }, 38 }, 39 } 40 41 args := []string{ 42 "-state", statePath, 43 "test_instance.foo", 44 "bar", 45 } 46 if code := c.Run(args); code != 0 { 47 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 48 } 49 50 if !p.ImportStateCalled { 51 t.Fatal("ImportState should be called") 52 } 53 54 testStateOutput(t, statePath, testImportStr) 55 } 56 57 func TestImport_providerConfig(t *testing.T) { 58 defer testChdir(t, testFixturePath("import-provider"))() 59 60 statePath := testTempFile(t) 61 62 p := testProvider() 63 ui := new(cli.MockUi) 64 c := &ImportCommand{ 65 Meta: Meta{ 66 testingOverrides: metaOverridesForProvider(p), 67 Ui: ui, 68 }, 69 } 70 71 p.ImportStateFn = nil 72 p.ImportStateReturn = []*terraform.InstanceState{ 73 &terraform.InstanceState{ 74 ID: "yay", 75 Ephemeral: terraform.EphemeralState{ 76 Type: "test_instance", 77 }, 78 }, 79 } 80 81 configured := false 82 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 83 configured = true 84 85 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 86 return fmt.Errorf("bad value: %#v", v) 87 } 88 89 return nil 90 } 91 92 args := []string{ 93 "-state", statePath, 94 "test_instance.foo", 95 "bar", 96 } 97 if code := c.Run(args); code != 0 { 98 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 99 } 100 101 // Verify that we were called 102 if !configured { 103 t.Fatal("Configure should be called") 104 } 105 106 if !p.ImportStateCalled { 107 t.Fatal("ImportState should be called") 108 } 109 110 testStateOutput(t, statePath, testImportStr) 111 } 112 113 // "remote" state provided by the "local" backend 114 func TestImport_remoteState(t *testing.T) { 115 td := tempDir(t) 116 copy.CopyDir(testFixturePath("import-provider-remote-state"), td) 117 defer os.RemoveAll(td) 118 defer testChdir(t, td)() 119 120 statePath := "imported.tfstate" 121 122 // init our backend 123 ui := new(cli.MockUi) 124 m := Meta{ 125 testingOverrides: metaOverridesForProvider(testProvider()), 126 Ui: ui, 127 } 128 129 ic := &InitCommand{ 130 Meta: m, 131 providerInstaller: &mockProviderInstaller{ 132 Providers: map[string][]string{ 133 "test": []string{"1.2.3"}, 134 }, 135 136 Dir: m.pluginDir(), 137 }, 138 } 139 140 if code := ic.Run([]string{}); code != 0 { 141 t.Fatalf("bad: \n%s", ui.ErrorWriter) 142 } 143 144 p := testProvider() 145 ui = new(cli.MockUi) 146 c := &ImportCommand{ 147 Meta: Meta{ 148 testingOverrides: metaOverridesForProvider(p), 149 Ui: ui, 150 }, 151 } 152 153 p.ImportStateFn = nil 154 p.ImportStateReturn = []*terraform.InstanceState{ 155 &terraform.InstanceState{ 156 ID: "yay", 157 Ephemeral: terraform.EphemeralState{ 158 Type: "test_instance", 159 }, 160 }, 161 } 162 163 configured := false 164 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 165 configured = true 166 167 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 168 return fmt.Errorf("bad value: %#v", v) 169 } 170 171 return nil 172 } 173 174 args := []string{ 175 "test_instance.foo", 176 "bar", 177 } 178 if code := c.Run(args); code != 0 { 179 fmt.Println(ui.OutputWriter) 180 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 181 } 182 183 // Verify that we were called 184 if !configured { 185 t.Fatal("Configure should be called") 186 } 187 188 if !p.ImportStateCalled { 189 t.Fatal("ImportState should be called") 190 } 191 192 testStateOutput(t, statePath, testImportStr) 193 } 194 195 func TestImport_providerConfigWithVar(t *testing.T) { 196 defer testChdir(t, testFixturePath("import-provider-var"))() 197 198 statePath := testTempFile(t) 199 200 p := testProvider() 201 ui := new(cli.MockUi) 202 c := &ImportCommand{ 203 Meta: Meta{ 204 testingOverrides: metaOverridesForProvider(p), 205 Ui: ui, 206 }, 207 } 208 209 p.ImportStateFn = nil 210 p.ImportStateReturn = []*terraform.InstanceState{ 211 &terraform.InstanceState{ 212 ID: "yay", 213 Ephemeral: terraform.EphemeralState{ 214 Type: "test_instance", 215 }, 216 }, 217 } 218 219 configured := false 220 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 221 configured = true 222 223 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 224 return fmt.Errorf("bad value: %#v", v) 225 } 226 227 return nil 228 } 229 230 args := []string{ 231 "-state", statePath, 232 "-var", "foo=bar", 233 "test_instance.foo", 234 "bar", 235 } 236 if code := c.Run(args); code != 0 { 237 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 238 } 239 240 // Verify that we were called 241 if !configured { 242 t.Fatal("Configure should be called") 243 } 244 245 if !p.ImportStateCalled { 246 t.Fatal("ImportState should be called") 247 } 248 249 testStateOutput(t, statePath, testImportStr) 250 } 251 252 func TestImport_providerConfigWithVarDefault(t *testing.T) { 253 defer testChdir(t, testFixturePath("import-provider-var-default"))() 254 255 statePath := testTempFile(t) 256 257 p := testProvider() 258 ui := new(cli.MockUi) 259 c := &ImportCommand{ 260 Meta: Meta{ 261 testingOverrides: metaOverridesForProvider(p), 262 Ui: ui, 263 }, 264 } 265 266 p.ImportStateFn = nil 267 p.ImportStateReturn = []*terraform.InstanceState{ 268 &terraform.InstanceState{ 269 ID: "yay", 270 Ephemeral: terraform.EphemeralState{ 271 Type: "test_instance", 272 }, 273 }, 274 } 275 276 configured := false 277 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 278 configured = true 279 280 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 281 return fmt.Errorf("bad value: %#v", v) 282 } 283 284 return nil 285 } 286 287 args := []string{ 288 "-state", statePath, 289 "test_instance.foo", 290 "bar", 291 } 292 if code := c.Run(args); code != 0 { 293 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 294 } 295 296 // Verify that we were called 297 if !configured { 298 t.Fatal("Configure should be called") 299 } 300 301 if !p.ImportStateCalled { 302 t.Fatal("ImportState should be called") 303 } 304 305 testStateOutput(t, statePath, testImportStr) 306 } 307 308 func TestImport_providerConfigWithVarFile(t *testing.T) { 309 defer testChdir(t, testFixturePath("import-provider-var-file"))() 310 311 statePath := testTempFile(t) 312 313 p := testProvider() 314 ui := new(cli.MockUi) 315 c := &ImportCommand{ 316 Meta: Meta{ 317 testingOverrides: metaOverridesForProvider(p), 318 Ui: ui, 319 }, 320 } 321 322 p.ImportStateFn = nil 323 p.ImportStateReturn = []*terraform.InstanceState{ 324 &terraform.InstanceState{ 325 ID: "yay", 326 Ephemeral: terraform.EphemeralState{ 327 Type: "test_instance", 328 }, 329 }, 330 } 331 332 configured := false 333 p.ConfigureFn = func(c *terraform.ResourceConfig) error { 334 configured = true 335 336 if v, ok := c.Get("foo"); !ok || v.(string) != "bar" { 337 return fmt.Errorf("bad value: %#v", v) 338 } 339 340 return nil 341 } 342 343 args := []string{ 344 "-state", statePath, 345 "-var-file", "blah.tfvars", 346 "test_instance.foo", 347 "bar", 348 } 349 if code := c.Run(args); code != 0 { 350 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 351 } 352 353 // Verify that we were called 354 if !configured { 355 t.Fatal("Configure should be called") 356 } 357 358 if !p.ImportStateCalled { 359 t.Fatal("ImportState should be called") 360 } 361 362 testStateOutput(t, statePath, testImportStr) 363 } 364 365 func TestImport_customProvider(t *testing.T) { 366 defer testChdir(t, testFixturePath("import-provider-aliased"))() 367 368 statePath := testTempFile(t) 369 370 p := testProvider() 371 ui := new(cli.MockUi) 372 c := &ImportCommand{ 373 Meta: Meta{ 374 testingOverrides: metaOverridesForProvider(p), 375 Ui: ui, 376 }, 377 } 378 379 p.ImportStateFn = nil 380 p.ImportStateReturn = []*terraform.InstanceState{ 381 &terraform.InstanceState{ 382 ID: "yay", 383 Ephemeral: terraform.EphemeralState{ 384 Type: "test_instance", 385 }, 386 }, 387 } 388 389 args := []string{ 390 "-provider", "test.alias", 391 "-state", statePath, 392 "test_instance.foo", 393 "bar", 394 } 395 if code := c.Run(args); code != 0 { 396 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 397 } 398 399 if !p.ImportStateCalled { 400 t.Fatal("ImportState should be called") 401 } 402 403 testStateOutput(t, statePath, testImportCustomProviderStr) 404 } 405 406 func TestImport_allowMissingResourceConfig(t *testing.T) { 407 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 408 409 statePath := testTempFile(t) 410 411 p := testProvider() 412 ui := new(cli.MockUi) 413 c := &ImportCommand{ 414 Meta: Meta{ 415 testingOverrides: metaOverridesForProvider(p), 416 Ui: ui, 417 }, 418 } 419 420 p.ImportStateFn = nil 421 p.ImportStateReturn = []*terraform.InstanceState{ 422 { 423 ID: "yay", 424 Ephemeral: terraform.EphemeralState{ 425 Type: "test_instance", 426 }, 427 }, 428 } 429 430 args := []string{ 431 "-state", statePath, 432 "-allow-missing-config", 433 "test_instance.foo", 434 "bar", 435 } 436 if code := c.Run(args); code != 0 { 437 t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) 438 } 439 440 if !p.ImportStateCalled { 441 t.Fatal("ImportState should be called") 442 } 443 444 testStateOutput(t, statePath, testImportStr) 445 } 446 447 func TestImport_emptyConfig(t *testing.T) { 448 defer testChdir(t, testFixturePath("empty"))() 449 450 statePath := testTempFile(t) 451 452 p := testProvider() 453 ui := new(cli.MockUi) 454 c := &ImportCommand{ 455 Meta: Meta{ 456 testingOverrides: metaOverridesForProvider(p), 457 Ui: ui, 458 }, 459 } 460 461 args := []string{ 462 "-state", statePath, 463 "test_instance.foo", 464 "bar", 465 } 466 code := c.Run(args) 467 if code != 1 { 468 t.Fatalf("import succeeded; expected failure") 469 } 470 471 msg := ui.ErrorWriter.String() 472 if want := `No Terraform configuration files`; !strings.Contains(msg, want) { 473 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 474 } 475 } 476 477 func TestImport_missingResourceConfig(t *testing.T) { 478 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 479 480 statePath := testTempFile(t) 481 482 p := testProvider() 483 ui := new(cli.MockUi) 484 c := &ImportCommand{ 485 Meta: Meta{ 486 testingOverrides: metaOverridesForProvider(p), 487 Ui: ui, 488 }, 489 } 490 491 args := []string{ 492 "-state", statePath, 493 "test_instance.foo", 494 "bar", 495 } 496 code := c.Run(args) 497 if code != 1 { 498 t.Fatalf("import succeeded; expected failure") 499 } 500 501 msg := ui.ErrorWriter.String() 502 if want := `resource address "test_instance.foo" does not exist`; !strings.Contains(msg, want) { 503 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 504 } 505 } 506 507 func TestImport_missingModuleConfig(t *testing.T) { 508 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 509 510 statePath := testTempFile(t) 511 512 p := testProvider() 513 ui := new(cli.MockUi) 514 c := &ImportCommand{ 515 Meta: Meta{ 516 testingOverrides: metaOverridesForProvider(p), 517 Ui: ui, 518 }, 519 } 520 521 args := []string{ 522 "-state", statePath, 523 "module.baz.test_instance.foo", 524 "bar", 525 } 526 code := c.Run(args) 527 if code != 1 { 528 t.Fatalf("import succeeded; expected failure") 529 } 530 531 msg := ui.ErrorWriter.String() 532 if want := `module.baz is not defined in the configuration`; !strings.Contains(msg, want) { 533 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 534 } 535 } 536 537 func TestImport_dataResource(t *testing.T) { 538 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 539 540 statePath := testTempFile(t) 541 542 p := testProvider() 543 ui := new(cli.MockUi) 544 c := &ImportCommand{ 545 Meta: Meta{ 546 testingOverrides: metaOverridesForProvider(p), 547 Ui: ui, 548 }, 549 } 550 551 args := []string{ 552 "-state", statePath, 553 "data.test_data_source.foo", 554 "bar", 555 } 556 code := c.Run(args) 557 if code != 1 { 558 t.Fatalf("import succeeded; expected failure") 559 } 560 561 msg := ui.ErrorWriter.String() 562 if want := `resource address must refer to a managed resource`; !strings.Contains(msg, want) { 563 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 564 } 565 } 566 567 func TestImport_invalidResourceAddr(t *testing.T) { 568 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 569 570 statePath := testTempFile(t) 571 572 p := testProvider() 573 ui := new(cli.MockUi) 574 c := &ImportCommand{ 575 Meta: Meta{ 576 testingOverrides: metaOverridesForProvider(p), 577 Ui: ui, 578 }, 579 } 580 581 args := []string{ 582 "-state", statePath, 583 "bananas", 584 "bar", 585 } 586 code := c.Run(args) 587 if code != 1 { 588 t.Fatalf("import succeeded; expected failure") 589 } 590 591 msg := ui.ErrorWriter.String() 592 if want := `invalid resource address "bananas"`; !strings.Contains(msg, want) { 593 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 594 } 595 } 596 597 func TestImport_targetIsModule(t *testing.T) { 598 defer testChdir(t, testFixturePath("import-missing-resource-config"))() 599 600 statePath := testTempFile(t) 601 602 p := testProvider() 603 ui := new(cli.MockUi) 604 c := &ImportCommand{ 605 Meta: Meta{ 606 testingOverrides: metaOverridesForProvider(p), 607 Ui: ui, 608 }, 609 } 610 611 args := []string{ 612 "-state", statePath, 613 "module.foo", 614 "bar", 615 } 616 code := c.Run(args) 617 if code != 1 { 618 t.Fatalf("import succeeded; expected failure") 619 } 620 621 msg := ui.ErrorWriter.String() 622 if want := `resource address must include a full resource spec`; !strings.Contains(msg, want) { 623 t.Errorf("incorrect message\nwant substring: %s\ngot:\n%s", want, msg) 624 } 625 } 626 627 // make sure we search the full plugin path during import 628 func TestImport_pluginDir(t *testing.T) { 629 td := tempDir(t) 630 copy.CopyDir(testFixturePath("import-provider"), td) 631 defer os.RemoveAll(td) 632 defer testChdir(t, td)() 633 634 // make a fake provider in a custom plugin directory 635 if err := os.Mkdir("plugins", 0755); err != nil { 636 t.Fatal(err) 637 } 638 if err := ioutil.WriteFile("plugins/terraform-provider-test_v1.1.1_x4", []byte("invalid binary"), 0755); err != nil { 639 t.Fatal(err) 640 } 641 642 ui := new(cli.MockUi) 643 c := &ImportCommand{ 644 Meta: Meta{ 645 Ui: ui, 646 }, 647 } 648 649 // store our custom plugin path, which would normally happen during init 650 if err := c.storePluginPath([]string{"./plugins"}); err != nil { 651 t.Fatal(err) 652 } 653 654 // Now we need to go through some plugin init. 655 // This discovers our fake plugin and writes the lock file. 656 initCmd := &InitCommand{ 657 Meta: Meta{ 658 pluginPath: []string{"./plugins"}, 659 Ui: new(cli.MockUi), 660 }, 661 providerInstaller: &discovery.ProviderInstaller{ 662 PluginProtocolVersion: plugin.Handshake.ProtocolVersion, 663 }, 664 } 665 if err := initCmd.getProviders(".", nil, false); err != nil { 666 t.Fatal(err) 667 } 668 669 args := []string{ 670 "test_instance.foo", 671 "bar", 672 } 673 if code := c.Run(args); code == 0 { 674 t.Fatalf("expected error, got: %s", ui.OutputWriter) 675 } 676 677 outMsg := ui.OutputWriter.String() 678 // if we were missing a plugin, the output will have some explanation 679 // about requirements. If discovery starts verifying binary compatibility, 680 // we will need to write a dummy provider above. 681 if strings.Contains(outMsg, "requirements") { 682 t.Fatal("unexpected output:", outMsg) 683 } 684 685 // We wanted a plugin execution error, rather than a requirement error. 686 // Looking for "exec" in the error should suffice for now. 687 errMsg := ui.ErrorWriter.String() 688 if !strings.Contains(errMsg, "exec") { 689 t.Fatal("unexpected error:", errMsg) 690 } 691 } 692 693 const testImportStr = ` 694 test_instance.foo: 695 ID = yay 696 provider = provider.test 697 ` 698 699 const testImportCustomProviderStr = ` 700 test_instance.foo: 701 ID = yay 702 provider = provider.test.alias 703 `