github.com/sixgill/terraform@v0.9.0-beta2.0.20170316214032-033f6226ae50/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 // PreventPostDestroyRefresh can be set to true for cases where data sources 155 // are tested alongside real resources 156 PreventPostDestroyRefresh bool 157 158 //--------------------------------------------------------------- 159 // ImportState testing 160 //--------------------------------------------------------------- 161 162 // ImportState, if true, will test the functionality of ImportState 163 // by importing the resource with ResourceName (must be set) and the 164 // ID of that resource. 165 ImportState bool 166 167 // ImportStateId is the ID to perform an ImportState operation with. 168 // This is optional. If it isn't set, then the resource ID is automatically 169 // determined by inspecting the state for ResourceName's ID. 170 ImportStateId string 171 172 // ImportStateCheck checks the results of ImportState. It should be 173 // used to verify that the resulting value of ImportState has the 174 // proper resources, IDs, and attributes. 175 ImportStateCheck ImportStateCheckFunc 176 177 // ImportStateVerify, if true, will also check that the state values 178 // that are finally put into the state after import match for all the 179 // IDs returned by the Import. 180 // 181 // ImportStateVerifyIgnore are fields that should not be verified to 182 // be equal. These can be set to ephemeral fields or fields that can't 183 // be refreshed and don't matter. 184 ImportStateVerify bool 185 ImportStateVerifyIgnore []string 186 } 187 188 // Test performs an acceptance test on a resource. 189 // 190 // Tests are not run unless an environmental variable "TF_ACC" is 191 // set to some non-empty value. This is to avoid test cases surprising 192 // a user by creating real resources. 193 // 194 // Tests will fail unless the verbose flag (`go test -v`, or explicitly 195 // the "-test.v" flag) is set. Because some acceptance tests take quite 196 // long, we require the verbose flag so users are able to see progress 197 // output. 198 func Test(t TestT, c TestCase) { 199 // We only run acceptance tests if an env var is set because they're 200 // slow and generally require some outside configuration. You can opt out 201 // of this with OverrideEnvVar on individual TestCases. 202 if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest { 203 t.Skip(fmt.Sprintf( 204 "Acceptance tests skipped unless env '%s' set", 205 TestEnvVar)) 206 return 207 } 208 209 logWriter, err := logging.LogOutput() 210 if err != nil { 211 t.Error(fmt.Errorf("error setting up logging: %s", err)) 212 } 213 log.SetOutput(logWriter) 214 215 // We require verbose mode so that the user knows what is going on. 216 if !testTesting && !testing.Verbose() && !c.IsUnitTest { 217 t.Fatal("Acceptance tests must be run with the -v flag on tests") 218 return 219 } 220 221 // Run the PreCheck if we have it 222 if c.PreCheck != nil { 223 c.PreCheck() 224 } 225 226 ctxProviders, err := testProviderFactories(c) 227 if err != nil { 228 t.Fatal(err) 229 } 230 opts := terraform.ContextOpts{Providers: ctxProviders} 231 232 // A single state variable to track the lifecycle, starting with no state 233 var state *terraform.State 234 235 // Go through each step and run it 236 var idRefreshCheck *terraform.ResourceState 237 idRefresh := c.IDRefreshName != "" 238 errored := false 239 for i, step := range c.Steps { 240 var err error 241 log.Printf("[WARN] Test: Executing step %d", i) 242 243 // Determine the test mode to execute 244 if step.Config != "" { 245 state, err = testStepConfig(opts, state, step) 246 } else if step.ImportState { 247 state, err = testStepImportState(opts, state, step) 248 } else { 249 err = fmt.Errorf( 250 "unknown test mode for step. Please see TestStep docs\n\n%#v", 251 step) 252 } 253 254 // If there was an error, exit 255 if err != nil { 256 // Perhaps we expected an error? Check if it matches 257 if step.ExpectError != nil { 258 if !step.ExpectError.MatchString(err.Error()) { 259 errored = true 260 t.Error(fmt.Sprintf( 261 "Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n", 262 i, err, step.ExpectError)) 263 break 264 } 265 } else { 266 errored = true 267 t.Error(fmt.Sprintf( 268 "Step %d error: %s", i, err)) 269 break 270 } 271 } 272 273 // If we've never checked an id-only refresh and our state isn't 274 // empty, find the first resource and test it. 275 if idRefresh && idRefreshCheck == nil && !state.Empty() { 276 // Find the first non-nil resource in the state 277 for _, m := range state.Modules { 278 if len(m.Resources) > 0 { 279 if v, ok := m.Resources[c.IDRefreshName]; ok { 280 idRefreshCheck = v 281 } 282 283 break 284 } 285 } 286 287 // If we have an instance to check for refreshes, do it 288 // immediately. We do it in the middle of another test 289 // because it shouldn't affect the overall state (refresh 290 // is read-only semantically) and we want to fail early if 291 // this fails. If refresh isn't read-only, then this will have 292 // caught a different bug. 293 if idRefreshCheck != nil { 294 log.Printf( 295 "[WARN] Test: Running ID-only refresh check on %s", 296 idRefreshCheck.Primary.ID) 297 if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil { 298 log.Printf("[ERROR] Test: ID-only test failed: %s", err) 299 t.Error(fmt.Sprintf( 300 "[ERROR] Test: ID-only test failed: %s", err)) 301 break 302 } 303 } 304 } 305 } 306 307 // If we never checked an id-only refresh, it is a failure. 308 if idRefresh { 309 if !errored && len(c.Steps) > 0 && idRefreshCheck == nil { 310 t.Error("ID-only refresh check never ran.") 311 } 312 } 313 314 // If we have a state, then run the destroy 315 if state != nil { 316 lastStep := c.Steps[len(c.Steps)-1] 317 destroyStep := TestStep{ 318 Config: lastStep.Config, 319 Check: c.CheckDestroy, 320 Destroy: true, 321 PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, 322 } 323 324 log.Printf("[WARN] Test: Executing destroy step") 325 state, err := testStep(opts, state, destroyStep) 326 if err != nil { 327 t.Error(fmt.Sprintf( 328 "Error destroying resource! WARNING: Dangling resources\n"+ 329 "may exist. The full state and error is shown below.\n\n"+ 330 "Error: %s\n\nState: %s", 331 err, 332 state)) 333 } 334 } else { 335 log.Printf("[WARN] Skipping destroy test since there is no state.") 336 } 337 } 338 339 // testProviderFactories is a helper to build the ResourceProviderFactory map 340 // with pre instantiated ResourceProviders, so that we can reset them for the 341 // test, while only calling the factory function once. 342 // Any errors are stored so that they can be returned by the factory in 343 // terraform to match non-test behavior. 344 func testProviderFactories(c TestCase) (map[string]terraform.ResourceProviderFactory, error) { 345 ctxProviders := make(map[string]terraform.ResourceProviderFactory) 346 347 // add any fixed providers 348 for k, p := range c.Providers { 349 ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p) 350 } 351 352 // call any factory functions and store the result. 353 for k, pf := range c.ProviderFactories { 354 p, err := pf() 355 ctxProviders[k] = func() (terraform.ResourceProvider, error) { 356 return p, err 357 } 358 } 359 360 // reset the providers if needed 361 for k, pf := range ctxProviders { 362 // we can ignore any errors here, if we don't have a provider to reset 363 // the error will be handled later 364 p, _ := pf() 365 if p, ok := p.(TestProvider); ok { 366 err := p.TestReset() 367 if err != nil { 368 return nil, fmt.Errorf("[ERROR] failed to reset provider %q: %s", k, err) 369 } 370 } 371 } 372 373 return ctxProviders, nil 374 } 375 376 // UnitTest is a helper to force the acceptance testing harness to run in the 377 // normal unit test suite. This should only be used for resource that don't 378 // have any external dependencies. 379 func UnitTest(t TestT, c TestCase) { 380 c.IsUnitTest = true 381 Test(t, c) 382 } 383 384 func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error { 385 // TODO: We guard by this right now so master doesn't explode. We 386 // need to remove this eventually to make this part of the normal tests. 387 if os.Getenv("TF_ACC_IDONLY") == "" { 388 return nil 389 } 390 391 name := fmt.Sprintf("%s.foo", r.Type) 392 393 // Build the state. The state is just the resource with an ID. There 394 // are no attributes. We only set what is needed to perform a refresh. 395 state := terraform.NewState() 396 state.RootModule().Resources[name] = &terraform.ResourceState{ 397 Type: r.Type, 398 Primary: &terraform.InstanceState{ 399 ID: r.Primary.ID, 400 }, 401 } 402 403 // Create the config module. We use the full config because Refresh 404 // doesn't have access to it and we may need things like provider 405 // configurations. The initial implementation of id-only checks used 406 // an empty config module, but that caused the aforementioned problems. 407 mod, err := testModule(opts, step) 408 if err != nil { 409 return err 410 } 411 412 // Initialize the context 413 opts.Module = mod 414 opts.State = state 415 ctx, err := terraform.NewContext(&opts) 416 if err != nil { 417 return err 418 } 419 if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { 420 if len(es) > 0 { 421 estrs := make([]string, len(es)) 422 for i, e := range es { 423 estrs[i] = e.Error() 424 } 425 return fmt.Errorf( 426 "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v", 427 ws, estrs) 428 } 429 430 log.Printf("[WARN] Config warnings: %#v", ws) 431 } 432 433 // Refresh! 434 state, err = ctx.Refresh() 435 if err != nil { 436 return fmt.Errorf("Error refreshing: %s", err) 437 } 438 439 // Verify attribute equivalence. 440 actualR := state.RootModule().Resources[name] 441 if actualR == nil { 442 return fmt.Errorf("Resource gone!") 443 } 444 if actualR.Primary == nil { 445 return fmt.Errorf("Resource has no primary instance") 446 } 447 actual := actualR.Primary.Attributes 448 expected := r.Primary.Attributes 449 // Remove fields we're ignoring 450 for _, v := range c.IDRefreshIgnore { 451 for k, _ := range actual { 452 if strings.HasPrefix(k, v) { 453 delete(actual, k) 454 } 455 } 456 for k, _ := range expected { 457 if strings.HasPrefix(k, v) { 458 delete(expected, k) 459 } 460 } 461 } 462 463 if !reflect.DeepEqual(actual, expected) { 464 // Determine only the different attributes 465 for k, v := range expected { 466 if av, ok := actual[k]; ok && v == av { 467 delete(expected, k) 468 delete(actual, k) 469 } 470 } 471 472 spewConf := spew.NewDefaultConfig() 473 spewConf.SortKeys = true 474 return fmt.Errorf( 475 "Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+ 476 "\n\n%s\n\n%s", 477 spewConf.Sdump(actual), spewConf.Sdump(expected)) 478 } 479 480 return nil 481 } 482 483 func testModule( 484 opts terraform.ContextOpts, 485 step TestStep) (*module.Tree, error) { 486 if step.PreConfig != nil { 487 step.PreConfig() 488 } 489 490 cfgPath, err := ioutil.TempDir("", "tf-test") 491 if err != nil { 492 return nil, fmt.Errorf( 493 "Error creating temporary directory for config: %s", err) 494 } 495 defer os.RemoveAll(cfgPath) 496 497 // Write the configuration 498 cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) 499 if err != nil { 500 return nil, fmt.Errorf( 501 "Error creating temporary file for config: %s", err) 502 } 503 504 _, err = io.Copy(cfgF, strings.NewReader(step.Config)) 505 cfgF.Close() 506 if err != nil { 507 return nil, fmt.Errorf( 508 "Error creating temporary file for config: %s", err) 509 } 510 511 // Parse the configuration 512 mod, err := module.NewTreeModule("", cfgPath) 513 if err != nil { 514 return nil, fmt.Errorf( 515 "Error loading configuration: %s", err) 516 } 517 518 // Load the modules 519 modStorage := &getter.FolderStorage{ 520 StorageDir: filepath.Join(cfgPath, ".tfmodules"), 521 } 522 err = mod.Load(modStorage, module.GetModeGet) 523 if err != nil { 524 return nil, fmt.Errorf("Error downloading modules: %s", err) 525 } 526 527 return mod, nil 528 } 529 530 func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) { 531 if c.ResourceName == "" { 532 return nil, fmt.Errorf("ResourceName must be set in TestStep") 533 } 534 535 for _, m := range state.Modules { 536 if len(m.Resources) > 0 { 537 if v, ok := m.Resources[c.ResourceName]; ok { 538 return v, nil 539 } 540 } 541 } 542 543 return nil, fmt.Errorf( 544 "Resource specified by ResourceName couldn't be found: %s", c.ResourceName) 545 } 546 547 // ComposeTestCheckFunc lets you compose multiple TestCheckFuncs into 548 // a single TestCheckFunc. 549 // 550 // As a user testing their provider, this lets you decompose your checks 551 // into smaller pieces more easily. 552 func ComposeTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { 553 return func(s *terraform.State) error { 554 for i, f := range fs { 555 if err := f(s); err != nil { 556 return fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err) 557 } 558 } 559 560 return nil 561 } 562 } 563 564 // ComposeAggregateTestCheckFunc lets you compose multiple TestCheckFuncs into 565 // a single TestCheckFunc. 566 // 567 // As a user testing their provider, this lets you decompose your checks 568 // into smaller pieces more easily. 569 // 570 // Unlike ComposeTestCheckFunc, ComposeAggergateTestCheckFunc runs _all_ of the 571 // TestCheckFuncs and aggregates failures. 572 func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc { 573 return func(s *terraform.State) error { 574 var result *multierror.Error 575 576 for i, f := range fs { 577 if err := f(s); err != nil { 578 result = multierror.Append(result, fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err)) 579 } 580 } 581 582 return result.ErrorOrNil() 583 } 584 } 585 586 // TestCheckResourceAttrSet is a TestCheckFunc which ensures a value 587 // exists in state for the given name/key combination. It is useful when 588 // testing that computed values were set, when it is not possible to 589 // know ahead of time what the values will be. 590 func TestCheckResourceAttrSet(name, key string) TestCheckFunc { 591 return func(s *terraform.State) error { 592 is, err := primaryInstanceState(s, name) 593 if err != nil { 594 return err 595 } 596 597 if val, ok := is.Attributes[key]; ok && val != "" { 598 return nil 599 } 600 601 return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) 602 } 603 } 604 605 // TestCheckResourceAttr is a TestCheckFunc which validates 606 // the value in state for the given name/key combination. 607 func TestCheckResourceAttr(name, key, value string) TestCheckFunc { 608 return func(s *terraform.State) error { 609 is, err := primaryInstanceState(s, name) 610 if err != nil { 611 return err 612 } 613 614 if v, ok := is.Attributes[key]; !ok || v != value { 615 if !ok { 616 return fmt.Errorf("%s: Attribute '%s' not found", name, key) 617 } 618 619 return fmt.Errorf( 620 "%s: Attribute '%s' expected %#v, got %#v", 621 name, 622 key, 623 value, 624 v) 625 } 626 627 return nil 628 } 629 } 630 631 // TestCheckNoResourceAttr is a TestCheckFunc which ensures that 632 // NO value exists in state for the given name/key combination. 633 func TestCheckNoResourceAttr(name, key string) TestCheckFunc { 634 return func(s *terraform.State) error { 635 is, err := primaryInstanceState(s, name) 636 if err != nil { 637 return err 638 } 639 640 if _, ok := is.Attributes[key]; ok { 641 return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) 642 } 643 644 return nil 645 } 646 } 647 648 // TestMatchResourceAttr is a TestCheckFunc which checks that the value 649 // in state for the given name/key combination matches the given regex. 650 func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { 651 return func(s *terraform.State) error { 652 is, err := primaryInstanceState(s, name) 653 if err != nil { 654 return err 655 } 656 657 if !r.MatchString(is.Attributes[key]) { 658 return fmt.Errorf( 659 "%s: Attribute '%s' didn't match %q, got %#v", 660 name, 661 key, 662 r.String(), 663 is.Attributes[key]) 664 } 665 666 return nil 667 } 668 } 669 670 // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the 671 // value is a pointer so that it can be updated while the test is running. 672 // It will only be dereferenced at the point this step is run. 673 func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckFunc { 674 return func(s *terraform.State) error { 675 return TestCheckResourceAttr(name, key, *value)(s) 676 } 677 } 678 679 // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values 680 // in state for a pair of name/key combinations are equal. 681 func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { 682 return func(s *terraform.State) error { 683 isFirst, err := primaryInstanceState(s, nameFirst) 684 if err != nil { 685 return err 686 } 687 vFirst, ok := isFirst.Attributes[keyFirst] 688 if !ok { 689 return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst) 690 } 691 692 isSecond, err := primaryInstanceState(s, nameSecond) 693 if err != nil { 694 return err 695 } 696 vSecond, ok := isSecond.Attributes[keySecond] 697 if !ok { 698 return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) 699 } 700 701 if vFirst != vSecond { 702 return fmt.Errorf( 703 "%s: Attribute '%s' expected %#v, got %#v", 704 nameFirst, 705 keyFirst, 706 vSecond, 707 vFirst) 708 } 709 710 return nil 711 } 712 } 713 714 // TestCheckOutput checks an output in the Terraform configuration 715 func TestCheckOutput(name, value string) TestCheckFunc { 716 return func(s *terraform.State) error { 717 ms := s.RootModule() 718 rs, ok := ms.Outputs[name] 719 if !ok { 720 return fmt.Errorf("Not found: %s", name) 721 } 722 723 if rs.Value != value { 724 return fmt.Errorf( 725 "Output '%s': expected %#v, got %#v", 726 name, 727 value, 728 rs) 729 } 730 731 return nil 732 } 733 } 734 735 func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc { 736 return func(s *terraform.State) error { 737 ms := s.RootModule() 738 rs, ok := ms.Outputs[name] 739 if !ok { 740 return fmt.Errorf("Not found: %s", name) 741 } 742 743 if !r.MatchString(rs.Value.(string)) { 744 return fmt.Errorf( 745 "Output '%s': %#v didn't match %q", 746 name, 747 rs, 748 r.String()) 749 } 750 751 return nil 752 } 753 } 754 755 // TestT is the interface used to handle the test lifecycle of a test. 756 // 757 // Users should just use a *testing.T object, which implements this. 758 type TestT interface { 759 Error(args ...interface{}) 760 Fatal(args ...interface{}) 761 Skip(args ...interface{}) 762 } 763 764 // This is set to true by unit tests to alter some behavior 765 var testTesting = false 766 767 // primaryInstanceState returns the primary instance state for the given resource name. 768 func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { 769 ms := s.RootModule() 770 rs, ok := ms.Resources[name] 771 if !ok { 772 return nil, fmt.Errorf("Not found: %s", name) 773 } 774 775 is := rs.Primary 776 if is == nil { 777 return nil, fmt.Errorf("No primary instance: %s", name) 778 } 779 780 return is, nil 781 }