github.com/dougneal/terraform@v0.6.15-0.20170330092735-b6a3840768a4/helper/resource/testing.go (about) 1 package resource 2 3 import ( 4 "fmt" 5 "io" 6 "io/ioutil" 7 "log" 8 "os" 9 "path/filepath" 10 "reflect" 11 "regexp" 12 "strings" 13 "testing" 14 15 "github.com/davecgh/go-spew/spew" 16 "github.com/hashicorp/go-getter" 17 "github.com/hashicorp/go-multierror" 18 "github.com/hashicorp/terraform/config/module" 19 "github.com/hashicorp/terraform/helper/logging" 20 "github.com/hashicorp/terraform/terraform" 21 ) 22 23 const TestEnvVar = "TF_ACC" 24 25 // TestProvider can be implemented by any ResourceProvider to provide custom 26 // reset functionality at the start of an acceptance test. 27 // The helper/schema Provider implements this interface. 28 type TestProvider interface { 29 TestReset() error 30 } 31 32 // TestCheckFunc is the callback type used with acceptance tests to check 33 // the state of a resource. The state passed in is the latest state known, 34 // or in the case of being after a destroy, it is the last known state when 35 // it was created. 36 type TestCheckFunc func(*terraform.State) error 37 38 // ImportStateCheckFunc is the check function for ImportState tests 39 type ImportStateCheckFunc func([]*terraform.InstanceState) error 40 41 // TestCase is a single acceptance test case used to test the apply/destroy 42 // lifecycle of a resource in a specific configuration. 43 // 44 // When the destroy plan is executed, the config from the last TestStep 45 // is used to plan it. 46 type TestCase struct { 47 // IsUnitTest allows a test to run regardless of the TF_ACC 48 // environment variable. This should be used with care - only for 49 // fast tests on local resources (e.g. remote state with a local 50 // backend) but can be used to increase confidence in correct 51 // operation of Terraform without waiting for a full acctest run. 52 IsUnitTest bool 53 54 // PreCheck, if non-nil, will be called before any test steps are 55 // executed. It will only be executed in the case that the steps 56 // would run, so it can be used for some validation before running 57 // acceptance tests, such as verifying that keys are setup. 58 PreCheck func() 59 60 // Providers is the ResourceProvider that will be under test. 61 // 62 // Alternately, ProviderFactories can be specified for the providers 63 // that are valid. This takes priority over Providers. 64 // 65 // The end effect of each is the same: specifying the providers that 66 // are used within the tests. 67 Providers map[string]terraform.ResourceProvider 68 ProviderFactories map[string]terraform.ResourceProviderFactory 69 70 // PreventPostDestroyRefresh can be set to true for cases where data sources 71 // are tested alongside real resources 72 PreventPostDestroyRefresh bool 73 74 // CheckDestroy is called after the resource is finally destroyed 75 // to allow the tester to test that the resource is truly gone. 76 CheckDestroy TestCheckFunc 77 78 // Steps are the apply sequences done within the context of the 79 // same state. Each step can have its own check to verify correctness. 80 Steps []TestStep 81 82 // The settings below control the "ID-only refresh test." This is 83 // an enabled-by-default test that tests that a refresh can be 84 // refreshed with only an ID to result in the same attributes. 85 // This validates completeness of Refresh. 86 // 87 // IDRefreshName is the name of the resource to check. This will 88 // default to the first non-nil primary resource in the state. 89 // 90 // IDRefreshIgnore is a list of configuration keys that will be ignored. 91 IDRefreshName string 92 IDRefreshIgnore []string 93 } 94 95 // TestStep is a single apply sequence of a test, done within the 96 // context of a state. 97 // 98 // Multiple TestSteps can be sequenced in a Test to allow testing 99 // potentially complex update logic. In general, simply create/destroy 100 // tests will only need one step. 101 type TestStep struct { 102 // ResourceName should be set to the name of the resource 103 // that is being tested. Example: "aws_instance.foo". Various test 104 // modes use this to auto-detect state information. 105 // 106 // This is only required if the test mode settings below say it is 107 // for the mode you're using. 108 ResourceName string 109 110 // PreConfig is called before the Config is applied to perform any per-step 111 // setup that needs to happen. This is called regardless of "test mode" 112 // below. 113 PreConfig func() 114 115 //--------------------------------------------------------------- 116 // Test modes. One of the following groups of settings must be 117 // set to determine what the test step will do. Ideally we would've 118 // used Go interfaces here but there are now hundreds of tests we don't 119 // want to re-type so instead we just determine which step logic 120 // to run based on what settings below are set. 121 //--------------------------------------------------------------- 122 123 //--------------------------------------------------------------- 124 // Plan, Apply testing 125 //--------------------------------------------------------------- 126 127 // Config a string of the configuration to give to Terraform. If this 128 // is set, then the TestCase will execute this step with the same logic 129 // as a `terraform apply`. 130 Config string 131 132 // Check is called after the Config is applied. Use this step to 133 // make your own API calls to check the status of things, and to 134 // inspect the format of the ResourceState itself. 135 // 136 // If an error is returned, the test will fail. In this case, a 137 // destroy plan will still be attempted. 138 // 139 // If this is nil, no check is done on this step. 140 Check TestCheckFunc 141 142 // Destroy will create a destroy plan if set to true. 143 Destroy bool 144 145 // ExpectNonEmptyPlan can be set to true for specific types of tests that are 146 // looking to verify that a diff occurs 147 ExpectNonEmptyPlan bool 148 149 // ExpectError allows the construction of test cases that we expect to fail 150 // with an error. The specified regexp must match against the error for the 151 // test to pass. 152 ExpectError *regexp.Regexp 153 154 // PlanOnly can be set to only run `plan` with this configuration, and not 155 // actually apply it. This is useful for ensuring config changes result in 156 // no-op plans 157 PlanOnly bool 158 159 // PreventPostDestroyRefresh can be set to true for cases where data sources 160 // are tested alongside real resources 161 PreventPostDestroyRefresh bool 162 163 //--------------------------------------------------------------- 164 // ImportState testing 165 //--------------------------------------------------------------- 166 167 // ImportState, if true, will test the functionality of ImportState 168 // by importing the resource with ResourceName (must be set) and the 169 // ID of that resource. 170 ImportState bool 171 172 // ImportStateId is the ID to perform an ImportState operation with. 173 // This is optional. If it isn't set, then the resource ID is automatically 174 // determined by inspecting the state for ResourceName's ID. 175 ImportStateId string 176 177 // ImportStateCheck checks the results of ImportState. It should be 178 // used to verify that the resulting value of ImportState has the 179 // proper resources, IDs, and attributes. 180 ImportStateCheck ImportStateCheckFunc 181 182 // ImportStateVerify, if true, will also check that the state values 183 // that are finally put into the state after import match for all the 184 // IDs returned by the Import. 185 // 186 // ImportStateVerifyIgnore are fields that should not be verified to 187 // be equal. These can be set to ephemeral fields or fields that can't 188 // be refreshed and don't matter. 189 ImportStateVerify bool 190 ImportStateVerifyIgnore []string 191 } 192 193 // Test performs an acceptance test on a resource. 194 // 195 // Tests are not run unless an environmental variable "TF_ACC" is 196 // set to some non-empty value. This is to avoid test cases surprising 197 // a user by creating real resources. 198 // 199 // Tests will fail unless the verbose flag (`go test -v`, or explicitly 200 // the "-test.v" flag) is set. Because some acceptance tests take quite 201 // long, we require the verbose flag so users are able to see progress 202 // output. 203 func Test(t TestT, c TestCase) { 204 // We only run acceptance tests if an env var is set because they're 205 // slow and generally require some outside configuration. You can opt out 206 // of this with OverrideEnvVar on individual TestCases. 207 if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest { 208 t.Skip(fmt.Sprintf( 209 "Acceptance tests skipped unless env '%s' set", 210 TestEnvVar)) 211 return 212 } 213 214 logWriter, err := logging.LogOutput() 215 if err != nil { 216 t.Error(fmt.Errorf("error setting up logging: %s", err)) 217 } 218 log.SetOutput(logWriter) 219 220 // We require verbose mode so that the user knows what is going on. 221 if !testTesting && !testing.Verbose() && !c.IsUnitTest { 222 t.Fatal("Acceptance tests must be run with the -v flag on tests") 223 return 224 } 225 226 // Run the PreCheck if we have it 227 if c.PreCheck != nil { 228 c.PreCheck() 229 } 230 231 ctxProviders, err := testProviderFactories(c) 232 if err != nil { 233 t.Fatal(err) 234 } 235 opts := terraform.ContextOpts{Providers: ctxProviders} 236 237 // A single state variable to track the lifecycle, starting with no state 238 var state *terraform.State 239 240 // Go through each step and run it 241 var idRefreshCheck *terraform.ResourceState 242 idRefresh := c.IDRefreshName != "" 243 errored := false 244 for i, step := range c.Steps { 245 var err error 246 log.Printf("[WARN] Test: Executing step %d", i) 247 248 // Determine the test mode to execute 249 if step.Config != "" { 250 state, err = testStepConfig(opts, state, step) 251 } else if step.ImportState { 252 state, err = testStepImportState(opts, state, step) 253 } else { 254 err = fmt.Errorf( 255 "unknown test mode for step. Please see TestStep docs\n\n%#v", 256 step) 257 } 258 259 // If there was an error, exit 260 if err != nil { 261 // Perhaps we expected an error? Check if it matches 262 if step.ExpectError != nil { 263 if !step.ExpectError.MatchString(err.Error()) { 264 errored = true 265 t.Error(fmt.Sprintf( 266 "Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n", 267 i, err, step.ExpectError)) 268 break 269 } 270 } else { 271 errored = true 272 t.Error(fmt.Sprintf( 273 "Step %d error: %s", i, err)) 274 break 275 } 276 } 277 278 // If we've never checked an id-only refresh and our state isn't 279 // empty, find the first resource and test it. 280 if idRefresh && idRefreshCheck == nil && !state.Empty() { 281 // Find the first non-nil resource in the state 282 for _, m := range state.Modules { 283 if len(m.Resources) > 0 { 284 if v, ok := m.Resources[c.IDRefreshName]; ok { 285 idRefreshCheck = v 286 } 287 288 break 289 } 290 } 291 292 // If we have an instance to check for refreshes, do it 293 // immediately. We do it in the middle of another test 294 // because it shouldn't affect the overall state (refresh 295 // is read-only semantically) and we want to fail early if 296 // this fails. If refresh isn't read-only, then this will have 297 // caught a different bug. 298 if idRefreshCheck != nil { 299 log.Printf( 300 "[WARN] Test: Running ID-only refresh check on %s", 301 idRefreshCheck.Primary.ID) 302 if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil { 303 log.Printf("[ERROR] Test: ID-only test failed: %s", err) 304 t.Error(fmt.Sprintf( 305 "[ERROR] Test: ID-only test failed: %s", err)) 306 break 307 } 308 } 309 } 310 } 311 312 // If we never checked an id-only refresh, it is a failure. 313 if idRefresh { 314 if !errored && len(c.Steps) > 0 && idRefreshCheck == nil { 315 t.Error("ID-only refresh check never ran.") 316 } 317 } 318 319 // If we have a state, then run the destroy 320 if state != nil { 321 lastStep := c.Steps[len(c.Steps)-1] 322 destroyStep := TestStep{ 323 Config: lastStep.Config, 324 Check: c.CheckDestroy, 325 Destroy: true, 326 PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, 327 } 328 329 log.Printf("[WARN] Test: Executing destroy step") 330 state, err := testStep(opts, state, destroyStep) 331 if err != nil { 332 t.Error(fmt.Sprintf( 333 "Error destroying resource! WARNING: Dangling resources\n"+ 334 "may exist. The full state and error is shown below.\n\n"+ 335 "Error: %s\n\nState: %s", 336 err, 337 state)) 338 } 339 } else { 340 log.Printf("[WARN] Skipping destroy test since there is no state.") 341 } 342 } 343 344 // testProviderFactories is a helper to build the ResourceProviderFactory map 345 // with pre instantiated ResourceProviders, so that we can reset them for the 346 // test, while only calling the factory function once. 347 // Any errors are stored so that they can be returned by the factory in 348 // terraform to match non-test behavior. 349 func testProviderFactories(c TestCase) (map[string]terraform.ResourceProviderFactory, error) { 350 ctxProviders := make(map[string]terraform.ResourceProviderFactory) 351 352 // add any fixed providers 353 for k, p := range c.Providers { 354 ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p) 355 } 356 357 // call any factory functions and store the result. 358 for k, pf := range c.ProviderFactories { 359 p, err := pf() 360 ctxProviders[k] = func() (terraform.ResourceProvider, error) { 361 return p, err 362 } 363 } 364 365 // reset the providers if needed 366 for k, pf := range ctxProviders { 367 // we can ignore any errors here, if we don't have a provider to reset 368 // the error will be handled later 369 p, _ := pf() 370 if p, ok := p.(TestProvider); ok { 371 err := p.TestReset() 372 if err != nil { 373 return nil, fmt.Errorf("[ERROR] failed to reset provider %q: %s", k, err) 374 } 375 } 376 } 377 378 return ctxProviders, nil 379 } 380 381 // UnitTest is a helper to force the acceptance testing harness to run in the 382 // normal unit test suite. This should only be used for resource that don't 383 // have any external dependencies. 384 func UnitTest(t TestT, c TestCase) { 385 c.IsUnitTest = true 386 Test(t, c) 387 } 388 389 func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error { 390 // TODO: We guard by this right now so master doesn't explode. We 391 // need to remove this eventually to make this part of the normal tests. 392 if os.Getenv("TF_ACC_IDONLY") == "" { 393 return nil 394 } 395 396 name := fmt.Sprintf("%s.foo", r.Type) 397 398 // Build the state. The state is just the resource with an ID. There 399 // are no attributes. We only set what is needed to perform a refresh. 400 state := terraform.NewState() 401 state.RootModule().Resources[name] = &terraform.ResourceState{ 402 Type: r.Type, 403 Primary: &terraform.InstanceState{ 404 ID: r.Primary.ID, 405 }, 406 } 407 408 // Create the config module. We use the full config because Refresh 409 // doesn't have access to it and we may need things like provider 410 // configurations. The initial implementation of id-only checks used 411 // an empty config module, but that caused the aforementioned problems. 412 mod, err := testModule(opts, step) 413 if err != nil { 414 return err 415 } 416 417 // Initialize the context 418 opts.Module = mod 419 opts.State = state 420 ctx, err := terraform.NewContext(&opts) 421 if err != nil { 422 return err 423 } 424 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { 425 if len(es) > 0 { 426 estrs := make([]string, len(es)) 427 for i, e := range es { 428 estrs[i] = e.Error() 429 } 430 return fmt.Errorf( 431 "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v", 432 ws, estrs) 433 } 434 435 log.Printf("[WARN] Config warnings: %#v", ws) 436 } 437 438 // Refresh! 439 state, err = ctx.Refresh() 440 if err != nil { 441 return fmt.Errorf("Error refreshing: %s", err) 442 } 443 444 // Verify attribute equivalence. 445 actualR := state.RootModule().Resources[name] 446 if actualR == nil { 447 return fmt.Errorf("Resource gone!") 448 } 449 if actualR.Primary == nil { 450 return fmt.Errorf("Resource has no primary instance") 451 } 452 actual := actualR.Primary.Attributes 453 expected := r.Primary.Attributes 454 // Remove fields we're ignoring 455 for _, v := range c.IDRefreshIgnore { 456 for k, _ := range actual { 457 if strings.HasPrefix(k, v) { 458 delete(actual, k) 459 } 460 } 461 for k, _ := range expected { 462 if strings.HasPrefix(k, v) { 463 delete(expected, k) 464 } 465 } 466 } 467 468 if !reflect.DeepEqual(actual, expected) { 469 // Determine only the different attributes 470 for k, v := range expected { 471 if av, ok := actual[k]; ok && v == av { 472 delete(expected, k) 473 delete(actual, k) 474 } 475 } 476 477 spewConf := spew.NewDefaultConfig() 478 spewConf.SortKeys = true 479 return fmt.Errorf( 480 "Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+ 481 "\n\n%s\n\n%s", 482 spewConf.Sdump(actual), spewConf.Sdump(expected)) 483 } 484 485 return nil 486 } 487 488 func testModule( 489 opts terraform.ContextOpts, 490 step TestStep) (*module.Tree, error) { 491 if step.PreConfig != nil { 492 step.PreConfig() 493 } 494 495 cfgPath, err := ioutil.TempDir("", "tf-test") 496 if err != nil { 497 return nil, fmt.Errorf( 498 "Error creating temporary directory for config: %s", err) 499 } 500 defer os.RemoveAll(cfgPath) 501 502 // Write the configuration 503 cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) 504 if err != nil { 505 return nil, fmt.Errorf( 506 "Error creating temporary file for config: %s", err) 507 } 508 509 _, err = io.Copy(cfgF, strings.NewReader(step.Config)) 510 cfgF.Close() 511 if err != nil { 512 return nil, fmt.Errorf( 513 "Error creating temporary file for config: %s", err) 514 } 515 516 // Parse the configuration 517 mod, err := module.NewTreeModule("", cfgPath) 518 if err != nil { 519 return nil, fmt.Errorf( 520 "Error loading configuration: %s", err) 521 } 522 523 // Load the modules 524 modStorage := &getter.FolderStorage{ 525 StorageDir: filepath.Join(cfgPath, ".tfmodules"), 526 } 527 err = mod.Load(modStorage, module.GetModeGet) 528 if err != nil { 529 return nil, fmt.Errorf("Error downloading modules: %s", err) 530 } 531 532 return mod, nil 533 } 534 535 func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) { 536 if c.ResourceName == "" { 537 return nil, fmt.Errorf("ResourceName must be set in TestStep") 538 } 539 540 for _, m := range state.Modules { 541 if len(m.Resources) > 0 { 542 if v, ok := m.Resources[c.ResourceName]; ok { 543 return v, nil 544 } 545 } 546 } 547 548 return nil, fmt.Errorf( 549 "Resource specified by ResourceName couldn't be found: %s", c.ResourceName) 550 } 551 552 // ComposeTestCheckFunc lets you compose multiple TestCheckFuncs into 553 // a single TestCheckFunc. 554 // 555 // As a user testing their provider, this lets you decompose your checks 556 // into smaller pieces more easily. 557 func ComposeTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { 558 return func(s *terraform.State) error { 559 for i, f := range fs { 560 if err := f(s); err != nil { 561 return fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err) 562 } 563 } 564 565 return nil 566 } 567 } 568 569 // ComposeAggregateTestCheckFunc lets you compose multiple TestCheckFuncs into 570 // a single TestCheckFunc. 571 // 572 // As a user testing their provider, this lets you decompose your checks 573 // into smaller pieces more easily. 574 // 575 // Unlike ComposeTestCheckFunc, ComposeAggergateTestCheckFunc runs _all_ of the 576 // TestCheckFuncs and aggregates failures. 577 func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { 578 return func(s *terraform.State) error { 579 var result *multierror.Error 580 581 for i, f := range fs { 582 if err := f(s); err != nil { 583 result = multierror.Append(result, fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err)) 584 } 585 } 586 587 return result.ErrorOrNil() 588 } 589 } 590 591 // TestCheckResourceAttrSet is a TestCheckFunc which ensures a value 592 // exists in state for the given name/key combination. It is useful when 593 // testing that computed values were set, when it is not possible to 594 // know ahead of time what the values will be. 595 func TestCheckResourceAttrSet(name, key string) TestCheckFunc { 596 return func(s *terraform.State) error { 597 is, err := primaryInstanceState(s, name) 598 if err != nil { 599 return err 600 } 601 602 if val, ok := is.Attributes[key]; ok && val != "" { 603 return nil 604 } 605 606 return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) 607 } 608 } 609 610 // TestCheckResourceAttr is a TestCheckFunc which validates 611 // the value in state for the given name/key combination. 612 func TestCheckResourceAttr(name, key, value string) TestCheckFunc { 613 return func(s *terraform.State) error { 614 is, err := primaryInstanceState(s, name) 615 if err != nil { 616 return err 617 } 618 619 if v, ok := is.Attributes[key]; !ok || v != value { 620 if !ok { 621 return fmt.Errorf("%s: Attribute '%s' not found", name, key) 622 } 623 624 return fmt.Errorf( 625 "%s: Attribute '%s' expected %#v, got %#v", 626 name, 627 key, 628 value, 629 v) 630 } 631 632 return nil 633 } 634 } 635 636 // TestCheckNoResourceAttr is a TestCheckFunc which ensures that 637 // NO value exists in state for the given name/key combination. 638 func TestCheckNoResourceAttr(name, key string) TestCheckFunc { 639 return func(s *terraform.State) error { 640 is, err := primaryInstanceState(s, name) 641 if err != nil { 642 return err 643 } 644 645 if _, ok := is.Attributes[key]; ok { 646 return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) 647 } 648 649 return nil 650 } 651 } 652 653 // TestMatchResourceAttr is a TestCheckFunc which checks that the value 654 // in state for the given name/key combination matches the given regex. 655 func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { 656 return func(s *terraform.State) error { 657 is, err := primaryInstanceState(s, name) 658 if err != nil { 659 return err 660 } 661 662 if !r.MatchString(is.Attributes[key]) { 663 return fmt.Errorf( 664 "%s: Attribute '%s' didn't match %q, got %#v", 665 name, 666 key, 667 r.String(), 668 is.Attributes[key]) 669 } 670 671 return nil 672 } 673 } 674 675 // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the 676 // value is a pointer so that it can be updated while the test is running. 677 // It will only be dereferenced at the point this step is run. 678 func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckFunc { 679 return func(s *terraform.State) error { 680 return TestCheckResourceAttr(name, key, *value)(s) 681 } 682 } 683 684 // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values 685 // in state for a pair of name/key combinations are equal. 686 func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { 687 return func(s *terraform.State) error { 688 isFirst, err := primaryInstanceState(s, nameFirst) 689 if err != nil { 690 return err 691 } 692 vFirst, ok := isFirst.Attributes[keyFirst] 693 if !ok { 694 return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst) 695 } 696 697 isSecond, err := primaryInstanceState(s, nameSecond) 698 if err != nil { 699 return err 700 } 701 vSecond, ok := isSecond.Attributes[keySecond] 702 if !ok { 703 return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) 704 } 705 706 if vFirst != vSecond { 707 return fmt.Errorf( 708 "%s: Attribute '%s' expected %#v, got %#v", 709 nameFirst, 710 keyFirst, 711 vSecond, 712 vFirst) 713 } 714 715 return nil 716 } 717 } 718 719 // TestCheckOutput checks an output in the Terraform configuration 720 func TestCheckOutput(name, value string) TestCheckFunc { 721 return func(s *terraform.State) error { 722 ms := s.RootModule() 723 rs, ok := ms.Outputs[name] 724 if !ok { 725 return fmt.Errorf("Not found: %s", name) 726 } 727 728 if rs.Value != value { 729 return fmt.Errorf( 730 "Output '%s': expected %#v, got %#v", 731 name, 732 value, 733 rs) 734 } 735 736 return nil 737 } 738 } 739 740 func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc { 741 return func(s *terraform.State) error { 742 ms := s.RootModule() 743 rs, ok := ms.Outputs[name] 744 if !ok { 745 return fmt.Errorf("Not found: %s", name) 746 } 747 748 if !r.MatchString(rs.Value.(string)) { 749 return fmt.Errorf( 750 "Output '%s': %#v didn't match %q", 751 name, 752 rs, 753 r.String()) 754 } 755 756 return nil 757 } 758 } 759 760 // TestT is the interface used to handle the test lifecycle of a test. 761 // 762 // Users should just use a *testing.T object, which implements this. 763 type TestT interface { 764 Error(args ...interface{}) 765 Fatal(args ...interface{}) 766 Skip(args ...interface{}) 767 } 768 769 // This is set to true by unit tests to alter some behavior 770 var testTesting = false 771 772 // primaryInstanceState returns the primary instance state for the given resource name. 773 func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { 774 ms := s.RootModule() 775 rs, ok := ms.Resources[name] 776 if !ok { 777 return nil, fmt.Errorf("Not found: %s", name) 778 } 779 780 is := rs.Primary 781 if is == nil { 782 return nil, fmt.Errorf("No primary instance: %s", name) 783 } 784 785 return is, nil 786 }