diff options
Diffstat (limited to 'vendor/github.com/hashicorp/terraform/helper/resource/testing.go')
-rw-r--r-- | vendor/github.com/hashicorp/terraform/helper/resource/testing.go | 348 |
1 files changed, 285 insertions, 63 deletions
diff --git a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go index d7de1a0..b97673f 100644 --- a/vendor/github.com/hashicorp/terraform/helper/resource/testing.go +++ b/vendor/github.com/hashicorp/terraform/helper/resource/testing.go | |||
@@ -11,11 +11,13 @@ import ( | |||
11 | "reflect" | 11 | "reflect" |
12 | "regexp" | 12 | "regexp" |
13 | "strings" | 13 | "strings" |
14 | "syscall" | ||
14 | "testing" | 15 | "testing" |
15 | 16 | ||
16 | "github.com/davecgh/go-spew/spew" | 17 | "github.com/davecgh/go-spew/spew" |
17 | "github.com/hashicorp/go-getter" | 18 | "github.com/hashicorp/errwrap" |
18 | "github.com/hashicorp/go-multierror" | 19 | "github.com/hashicorp/go-multierror" |
20 | "github.com/hashicorp/logutils" | ||
19 | "github.com/hashicorp/terraform/config/module" | 21 | "github.com/hashicorp/terraform/config/module" |
20 | "github.com/hashicorp/terraform/helper/logging" | 22 | "github.com/hashicorp/terraform/helper/logging" |
21 | "github.com/hashicorp/terraform/terraform" | 23 | "github.com/hashicorp/terraform/terraform" |
@@ -186,6 +188,10 @@ type TestCheckFunc func(*terraform.State) error | |||
186 | // ImportStateCheckFunc is the check function for ImportState tests | 188 | // ImportStateCheckFunc is the check function for ImportState tests |
187 | type ImportStateCheckFunc func([]*terraform.InstanceState) error | 189 | type ImportStateCheckFunc func([]*terraform.InstanceState) error |
188 | 190 | ||
191 | // ImportStateIdFunc is an ID generation function to help with complex ID | ||
192 | // generation for ImportState tests. | ||
193 | type ImportStateIdFunc func(*terraform.State) (string, error) | ||
194 | |||
189 | // TestCase is a single acceptance test case used to test the apply/destroy | 195 | // TestCase is a single acceptance test case used to test the apply/destroy |
190 | // lifecycle of a resource in a specific configuration. | 196 | // lifecycle of a resource in a specific configuration. |
191 | // | 197 | // |
@@ -260,6 +266,15 @@ type TestStep struct { | |||
260 | // below. | 266 | // below. |
261 | PreConfig func() | 267 | PreConfig func() |
262 | 268 | ||
269 | // Taint is a list of resource addresses to taint prior to the execution of | ||
270 | // the step. Be sure to only include this at a step where the referenced | ||
271 | // address will be present in state, as it will fail the test if the resource | ||
272 | // is missing. | ||
273 | // | ||
274 | // This option is ignored on ImportState tests, and currently only works for | ||
275 | // resources in the root module path. | ||
276 | Taint []string | ||
277 | |||
263 | //--------------------------------------------------------------- | 278 | //--------------------------------------------------------------- |
264 | // Test modes. One of the following groups of settings must be | 279 | // Test modes. One of the following groups of settings must be |
265 | // set to determine what the test step will do. Ideally we would've | 280 | // set to determine what the test step will do. Ideally we would've |
@@ -304,10 +319,19 @@ type TestStep struct { | |||
304 | // no-op plans | 319 | // no-op plans |
305 | PlanOnly bool | 320 | PlanOnly bool |
306 | 321 | ||
322 | // PreventDiskCleanup can be set to true for testing terraform modules which | ||
323 | // require access to disk at runtime. Note that this will leave files in the | ||
324 | // temp folder | ||
325 | PreventDiskCleanup bool | ||
326 | |||
307 | // PreventPostDestroyRefresh can be set to true for cases where data sources | 327 | // PreventPostDestroyRefresh can be set to true for cases where data sources |
308 | // are tested alongside real resources | 328 | // are tested alongside real resources |
309 | PreventPostDestroyRefresh bool | 329 | PreventPostDestroyRefresh bool |
310 | 330 | ||
331 | // SkipFunc is called before applying config, but after PreConfig | ||
332 | // This is useful for defining test steps with platform-dependent checks | ||
333 | SkipFunc func() (bool, error) | ||
334 | |||
311 | //--------------------------------------------------------------- | 335 | //--------------------------------------------------------------- |
312 | // ImportState testing | 336 | // ImportState testing |
313 | //--------------------------------------------------------------- | 337 | //--------------------------------------------------------------- |
@@ -329,6 +353,12 @@ type TestStep struct { | |||
329 | // the unset ImportStateId field. | 353 | // the unset ImportStateId field. |
330 | ImportStateIdPrefix string | 354 | ImportStateIdPrefix string |
331 | 355 | ||
356 | // ImportStateIdFunc is a function that can be used to dynamically generate | ||
357 | // the ID for the ImportState tests. It is sent the state, which can be | ||
358 | // checked to derive the attributes necessary and generate the string in the | ||
359 | // desired format. | ||
360 | ImportStateIdFunc ImportStateIdFunc | ||
361 | |||
332 | // ImportStateCheck checks the results of ImportState. It should be | 362 | // ImportStateCheck checks the results of ImportState. It should be |
333 | // used to verify that the resulting value of ImportState has the | 363 | // used to verify that the resulting value of ImportState has the |
334 | // proper resources, IDs, and attributes. | 364 | // proper resources, IDs, and attributes. |
@@ -345,6 +375,60 @@ type TestStep struct { | |||
345 | ImportStateVerifyIgnore []string | 375 | ImportStateVerifyIgnore []string |
346 | } | 376 | } |
347 | 377 | ||
378 | // Set to a file mask in sprintf format where %s is test name | ||
379 | const EnvLogPathMask = "TF_LOG_PATH_MASK" | ||
380 | |||
381 | func LogOutput(t TestT) (logOutput io.Writer, err error) { | ||
382 | logOutput = ioutil.Discard | ||
383 | |||
384 | logLevel := logging.LogLevel() | ||
385 | if logLevel == "" { | ||
386 | return | ||
387 | } | ||
388 | |||
389 | logOutput = os.Stderr | ||
390 | |||
391 | if logPath := os.Getenv(logging.EnvLogFile); logPath != "" { | ||
392 | var err error | ||
393 | logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) | ||
394 | if err != nil { | ||
395 | return nil, err | ||
396 | } | ||
397 | } | ||
398 | |||
399 | if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" { | ||
400 | // Escape special characters which may appear if we have subtests | ||
401 | testName := strings.Replace(t.Name(), "/", "__", -1) | ||
402 | |||
403 | logPath := fmt.Sprintf(logPathMask, testName) | ||
404 | var err error | ||
405 | logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666) | ||
406 | if err != nil { | ||
407 | return nil, err | ||
408 | } | ||
409 | } | ||
410 | |||
411 | // This was the default since the beginning | ||
412 | logOutput = &logutils.LevelFilter{ | ||
413 | Levels: logging.ValidLevels, | ||
414 | MinLevel: logutils.LogLevel(logLevel), | ||
415 | Writer: logOutput, | ||
416 | } | ||
417 | |||
418 | return | ||
419 | } | ||
420 | |||
421 | // ParallelTest performs an acceptance test on a resource, allowing concurrency | ||
422 | // with other ParallelTest. | ||
423 | // | ||
424 | // Tests will fail if they do not properly handle conditions to allow multiple | ||
425 | // tests to occur against the same resource or service (e.g. random naming). | ||
426 | // All other requirements of the Test function also apply to this function. | ||
427 | func ParallelTest(t TestT, c TestCase) { | ||
428 | t.Parallel() | ||
429 | Test(t, c) | ||
430 | } | ||
431 | |||
348 | // Test performs an acceptance test on a resource. | 432 | // Test performs an acceptance test on a resource. |
349 | // | 433 | // |
350 | // Tests are not run unless an environmental variable "TF_ACC" is | 434 | // Tests are not run unless an environmental variable "TF_ACC" is |
@@ -366,7 +450,7 @@ func Test(t TestT, c TestCase) { | |||
366 | return | 450 | return |
367 | } | 451 | } |
368 | 452 | ||
369 | logWriter, err := logging.LogOutput() | 453 | logWriter, err := LogOutput(t) |
370 | if err != nil { | 454 | if err != nil { |
371 | t.Error(fmt.Errorf("error setting up logging: %s", err)) | 455 | t.Error(fmt.Errorf("error setting up logging: %s", err)) |
372 | } | 456 | } |
@@ -398,7 +482,18 @@ func Test(t TestT, c TestCase) { | |||
398 | errored := false | 482 | errored := false |
399 | for i, step := range c.Steps { | 483 | for i, step := range c.Steps { |
400 | var err error | 484 | var err error |
401 | log.Printf("[WARN] Test: Executing step %d", i) | 485 | log.Printf("[DEBUG] Test: Executing step %d", i) |
486 | |||
487 | if step.SkipFunc != nil { | ||
488 | skip, err := step.SkipFunc() | ||
489 | if err != nil { | ||
490 | t.Fatal(err) | ||
491 | } | ||
492 | if skip { | ||
493 | log.Printf("[WARN] Skipping step %d", i) | ||
494 | continue | ||
495 | } | ||
496 | } | ||
402 | 497 | ||
403 | if step.Config == "" && !step.ImportState { | 498 | if step.Config == "" && !step.ImportState { |
404 | err = fmt.Errorf( | 499 | err = fmt.Errorf( |
@@ -418,6 +513,15 @@ func Test(t TestT, c TestCase) { | |||
418 | } | 513 | } |
419 | } | 514 | } |
420 | 515 | ||
516 | // If we expected an error, but did not get one, fail | ||
517 | if err == nil && step.ExpectError != nil { | ||
518 | errored = true | ||
519 | t.Error(fmt.Sprintf( | ||
520 | "Step %d, no error received, but expected a match to:\n\n%s\n\n", | ||
521 | i, step.ExpectError)) | ||
522 | break | ||
523 | } | ||
524 | |||
421 | // If there was an error, exit | 525 | // If there was an error, exit |
422 | if err != nil { | 526 | if err != nil { |
423 | // Perhaps we expected an error? Check if it matches | 527 | // Perhaps we expected an error? Check if it matches |
@@ -485,6 +589,7 @@ func Test(t TestT, c TestCase) { | |||
485 | Config: lastStep.Config, | 589 | Config: lastStep.Config, |
486 | Check: c.CheckDestroy, | 590 | Check: c.CheckDestroy, |
487 | Destroy: true, | 591 | Destroy: true, |
592 | PreventDiskCleanup: lastStep.PreventDiskCleanup, | ||
488 | PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, | 593 | PreventPostDestroyRefresh: c.PreventPostDestroyRefresh, |
489 | } | 594 | } |
490 | 595 | ||
@@ -593,18 +698,12 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r | |||
593 | if err != nil { | 698 | if err != nil { |
594 | return err | 699 | return err |
595 | } | 700 | } |
596 | if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 { | 701 | if diags := ctx.Validate(); len(diags) > 0 { |
597 | if len(es) > 0 { | 702 | if diags.HasErrors() { |
598 | estrs := make([]string, len(es)) | 703 | return errwrap.Wrapf("config is invalid: {{err}}", diags.Err()) |
599 | for i, e := range es { | ||
600 | estrs[i] = e.Error() | ||
601 | } | ||
602 | return fmt.Errorf( | ||
603 | "Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v", | ||
604 | ws, estrs) | ||
605 | } | 704 | } |
606 | 705 | ||
607 | log.Printf("[WARN] Config warnings: %#v", ws) | 706 | log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error()) |
608 | } | 707 | } |
609 | 708 | ||
610 | // Refresh! | 709 | // Refresh! |
@@ -657,9 +756,7 @@ func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r | |||
657 | return nil | 756 | return nil |
658 | } | 757 | } |
659 | 758 | ||
660 | func testModule( | 759 | func testModule(opts terraform.ContextOpts, step TestStep) (*module.Tree, error) { |
661 | opts terraform.ContextOpts, | ||
662 | step TestStep) (*module.Tree, error) { | ||
663 | if step.PreConfig != nil { | 760 | if step.PreConfig != nil { |
664 | step.PreConfig() | 761 | step.PreConfig() |
665 | } | 762 | } |
@@ -669,7 +766,12 @@ func testModule( | |||
669 | return nil, fmt.Errorf( | 766 | return nil, fmt.Errorf( |
670 | "Error creating temporary directory for config: %s", err) | 767 | "Error creating temporary directory for config: %s", err) |
671 | } | 768 | } |
672 | defer os.RemoveAll(cfgPath) | 769 | |
770 | if step.PreventDiskCleanup { | ||
771 | log.Printf("[INFO] Skipping defer os.RemoveAll call") | ||
772 | } else { | ||
773 | defer os.RemoveAll(cfgPath) | ||
774 | } | ||
673 | 775 | ||
674 | // Write the configuration | 776 | // Write the configuration |
675 | cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) | 777 | cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf")) |
@@ -693,10 +795,11 @@ func testModule( | |||
693 | } | 795 | } |
694 | 796 | ||
695 | // Load the modules | 797 | // Load the modules |
696 | modStorage := &getter.FolderStorage{ | 798 | modStorage := &module.Storage{ |
697 | StorageDir: filepath.Join(cfgPath, ".tfmodules"), | 799 | StorageDir: filepath.Join(cfgPath, ".tfmodules"), |
800 | Mode: module.GetModeGet, | ||
698 | } | 801 | } |
699 | err = mod.Load(modStorage, module.GetModeGet) | 802 | err = mod.Load(modStorage) |
700 | if err != nil { | 803 | if err != nil { |
701 | return nil, fmt.Errorf("Error downloading modules: %s", err) | 804 | return nil, fmt.Errorf("Error downloading modules: %s", err) |
702 | } | 805 | } |
@@ -771,12 +874,29 @@ func TestCheckResourceAttrSet(name, key string) TestCheckFunc { | |||
771 | return err | 874 | return err |
772 | } | 875 | } |
773 | 876 | ||
774 | if val, ok := is.Attributes[key]; ok && val != "" { | 877 | return testCheckResourceAttrSet(is, name, key) |
775 | return nil | 878 | } |
879 | } | ||
880 | |||
881 | // TestCheckModuleResourceAttrSet - as per TestCheckResourceAttrSet but with | ||
882 | // support for non-root modules | ||
883 | func TestCheckModuleResourceAttrSet(mp []string, name string, key string) TestCheckFunc { | ||
884 | return func(s *terraform.State) error { | ||
885 | is, err := modulePathPrimaryInstanceState(s, mp, name) | ||
886 | if err != nil { | ||
887 | return err | ||
776 | } | 888 | } |
777 | 889 | ||
890 | return testCheckResourceAttrSet(is, name, key) | ||
891 | } | ||
892 | } | ||
893 | |||
894 | func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key string) error { | ||
895 | if val, ok := is.Attributes[key]; !ok || val == "" { | ||
778 | return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) | 896 | return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key) |
779 | } | 897 | } |
898 | |||
899 | return nil | ||
780 | } | 900 | } |
781 | 901 | ||
782 | // TestCheckResourceAttr is a TestCheckFunc which validates | 902 | // TestCheckResourceAttr is a TestCheckFunc which validates |
@@ -788,21 +908,37 @@ func TestCheckResourceAttr(name, key, value string) TestCheckFunc { | |||
788 | return err | 908 | return err |
789 | } | 909 | } |
790 | 910 | ||
791 | if v, ok := is.Attributes[key]; !ok || v != value { | 911 | return testCheckResourceAttr(is, name, key, value) |
792 | if !ok { | 912 | } |
793 | return fmt.Errorf("%s: Attribute '%s' not found", name, key) | 913 | } |
794 | } | ||
795 | 914 | ||
796 | return fmt.Errorf( | 915 | // TestCheckModuleResourceAttr - as per TestCheckResourceAttr but with |
797 | "%s: Attribute '%s' expected %#v, got %#v", | 916 | // support for non-root modules |
798 | name, | 917 | func TestCheckModuleResourceAttr(mp []string, name string, key string, value string) TestCheckFunc { |
799 | key, | 918 | return func(s *terraform.State) error { |
800 | value, | 919 | is, err := modulePathPrimaryInstanceState(s, mp, name) |
801 | v) | 920 | if err != nil { |
921 | return err | ||
802 | } | 922 | } |
803 | 923 | ||
804 | return nil | 924 | return testCheckResourceAttr(is, name, key, value) |
925 | } | ||
926 | } | ||
927 | |||
928 | func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error { | ||
929 | if v, ok := is.Attributes[key]; !ok || v != value { | ||
930 | if !ok { | ||
931 | return fmt.Errorf("%s: Attribute '%s' not found", name, key) | ||
932 | } | ||
933 | |||
934 | return fmt.Errorf( | ||
935 | "%s: Attribute '%s' expected %#v, got %#v", | ||
936 | name, | ||
937 | key, | ||
938 | value, | ||
939 | v) | ||
805 | } | 940 | } |
941 | return nil | ||
806 | } | 942 | } |
807 | 943 | ||
808 | // TestCheckNoResourceAttr is a TestCheckFunc which ensures that | 944 | // TestCheckNoResourceAttr is a TestCheckFunc which ensures that |
@@ -814,14 +950,31 @@ func TestCheckNoResourceAttr(name, key string) TestCheckFunc { | |||
814 | return err | 950 | return err |
815 | } | 951 | } |
816 | 952 | ||
817 | if _, ok := is.Attributes[key]; ok { | 953 | return testCheckNoResourceAttr(is, name, key) |
818 | return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) | 954 | } |
955 | } | ||
956 | |||
957 | // TestCheckModuleNoResourceAttr - as per TestCheckNoResourceAttr but with | ||
958 | // support for non-root modules | ||
959 | func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestCheckFunc { | ||
960 | return func(s *terraform.State) error { | ||
961 | is, err := modulePathPrimaryInstanceState(s, mp, name) | ||
962 | if err != nil { | ||
963 | return err | ||
819 | } | 964 | } |
820 | 965 | ||
821 | return nil | 966 | return testCheckNoResourceAttr(is, name, key) |
822 | } | 967 | } |
823 | } | 968 | } |
824 | 969 | ||
970 | func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error { | ||
971 | if _, ok := is.Attributes[key]; ok { | ||
972 | return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key) | ||
973 | } | ||
974 | |||
975 | return nil | ||
976 | } | ||
977 | |||
825 | // TestMatchResourceAttr is a TestCheckFunc which checks that the value | 978 | // TestMatchResourceAttr is a TestCheckFunc which checks that the value |
826 | // in state for the given name/key combination matches the given regex. | 979 | // in state for the given name/key combination matches the given regex. |
827 | func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { | 980 | func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { |
@@ -831,17 +984,34 @@ func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc { | |||
831 | return err | 984 | return err |
832 | } | 985 | } |
833 | 986 | ||
834 | if !r.MatchString(is.Attributes[key]) { | 987 | return testMatchResourceAttr(is, name, key, r) |
835 | return fmt.Errorf( | 988 | } |
836 | "%s: Attribute '%s' didn't match %q, got %#v", | 989 | } |
837 | name, | 990 | |
838 | key, | 991 | // TestModuleMatchResourceAttr - as per TestMatchResourceAttr but with |
839 | r.String(), | 992 | // support for non-root modules |
840 | is.Attributes[key]) | 993 | func TestModuleMatchResourceAttr(mp []string, name string, key string, r *regexp.Regexp) TestCheckFunc { |
994 | return func(s *terraform.State) error { | ||
995 | is, err := modulePathPrimaryInstanceState(s, mp, name) | ||
996 | if err != nil { | ||
997 | return err | ||
841 | } | 998 | } |
842 | 999 | ||
843 | return nil | 1000 | return testMatchResourceAttr(is, name, key, r) |
1001 | } | ||
1002 | } | ||
1003 | |||
1004 | func testMatchResourceAttr(is *terraform.InstanceState, name string, key string, r *regexp.Regexp) error { | ||
1005 | if !r.MatchString(is.Attributes[key]) { | ||
1006 | return fmt.Errorf( | ||
1007 | "%s: Attribute '%s' didn't match %q, got %#v", | ||
1008 | name, | ||
1009 | key, | ||
1010 | r.String(), | ||
1011 | is.Attributes[key]) | ||
844 | } | 1012 | } |
1013 | |||
1014 | return nil | ||
845 | } | 1015 | } |
846 | 1016 | ||
847 | // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the | 1017 | // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the |
@@ -853,6 +1023,14 @@ func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckF | |||
853 | } | 1023 | } |
854 | } | 1024 | } |
855 | 1025 | ||
1026 | // TestCheckModuleResourceAttrPtr - as per TestCheckResourceAttrPtr but with | ||
1027 | // support for non-root modules | ||
1028 | func TestCheckModuleResourceAttrPtr(mp []string, name string, key string, value *string) TestCheckFunc { | ||
1029 | return func(s *terraform.State) error { | ||
1030 | return TestCheckModuleResourceAttr(mp, name, key, *value)(s) | ||
1031 | } | ||
1032 | } | ||
1033 | |||
856 | // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values | 1034 | // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values |
857 | // in state for a pair of name/key combinations are equal. | 1035 | // in state for a pair of name/key combinations are equal. |
858 | func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { | 1036 | func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc { |
@@ -861,33 +1039,57 @@ func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string | |||
861 | if err != nil { | 1039 | if err != nil { |
862 | return err | 1040 | return err |
863 | } | 1041 | } |
864 | vFirst, ok := isFirst.Attributes[keyFirst] | ||
865 | if !ok { | ||
866 | return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst) | ||
867 | } | ||
868 | 1042 | ||
869 | isSecond, err := primaryInstanceState(s, nameSecond) | 1043 | isSecond, err := primaryInstanceState(s, nameSecond) |
870 | if err != nil { | 1044 | if err != nil { |
871 | return err | 1045 | return err |
872 | } | 1046 | } |
873 | vSecond, ok := isSecond.Attributes[keySecond] | 1047 | |
874 | if !ok { | 1048 | return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) |
875 | return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) | 1049 | } |
1050 | } | ||
1051 | |||
1052 | // TestCheckModuleResourceAttrPair - as per TestCheckResourceAttrPair but with | ||
1053 | // support for non-root modules | ||
1054 | func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirst string, mpSecond []string, nameSecond string, keySecond string) TestCheckFunc { | ||
1055 | return func(s *terraform.State) error { | ||
1056 | isFirst, err := modulePathPrimaryInstanceState(s, mpFirst, nameFirst) | ||
1057 | if err != nil { | ||
1058 | return err | ||
876 | } | 1059 | } |
877 | 1060 | ||
878 | if vFirst != vSecond { | 1061 | isSecond, err := modulePathPrimaryInstanceState(s, mpSecond, nameSecond) |
879 | return fmt.Errorf( | 1062 | if err != nil { |
880 | "%s: Attribute '%s' expected %#v, got %#v", | 1063 | return err |
881 | nameFirst, | ||
882 | keyFirst, | ||
883 | vSecond, | ||
884 | vFirst) | ||
885 | } | 1064 | } |
886 | 1065 | ||
887 | return nil | 1066 | return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond) |
888 | } | 1067 | } |
889 | } | 1068 | } |
890 | 1069 | ||
1070 | func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst string, keyFirst string, isSecond *terraform.InstanceState, nameSecond string, keySecond string) error { | ||
1071 | vFirst, ok := isFirst.Attributes[keyFirst] | ||
1072 | if !ok { | ||
1073 | return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst) | ||
1074 | } | ||
1075 | |||
1076 | vSecond, ok := isSecond.Attributes[keySecond] | ||
1077 | if !ok { | ||
1078 | return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond) | ||
1079 | } | ||
1080 | |||
1081 | if vFirst != vSecond { | ||
1082 | return fmt.Errorf( | ||
1083 | "%s: Attribute '%s' expected %#v, got %#v", | ||
1084 | nameFirst, | ||
1085 | keyFirst, | ||
1086 | vSecond, | ||
1087 | vFirst) | ||
1088 | } | ||
1089 | |||
1090 | return nil | ||
1091 | } | ||
1092 | |||
891 | // TestCheckOutput checks an output in the Terraform configuration | 1093 | // TestCheckOutput checks an output in the Terraform configuration |
892 | func TestCheckOutput(name, value string) TestCheckFunc { | 1094 | func TestCheckOutput(name, value string) TestCheckFunc { |
893 | return func(s *terraform.State) error { | 1095 | return func(s *terraform.State) error { |
@@ -936,23 +1138,43 @@ type TestT interface { | |||
936 | Error(args ...interface{}) | 1138 | Error(args ...interface{}) |
937 | Fatal(args ...interface{}) | 1139 | Fatal(args ...interface{}) |
938 | Skip(args ...interface{}) | 1140 | Skip(args ...interface{}) |
1141 | Name() string | ||
1142 | Parallel() | ||
939 | } | 1143 | } |
940 | 1144 | ||
941 | // This is set to true by unit tests to alter some behavior | 1145 | // This is set to true by unit tests to alter some behavior |
942 | var testTesting = false | 1146 | var testTesting = false |
943 | 1147 | ||
944 | // primaryInstanceState returns the primary instance state for the given resource name. | 1148 | // modulePrimaryInstanceState returns the instance state for the given resource |
945 | func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { | 1149 | // name in a ModuleState |
946 | ms := s.RootModule() | 1150 | func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) { |
947 | rs, ok := ms.Resources[name] | 1151 | rs, ok := ms.Resources[name] |
948 | if !ok { | 1152 | if !ok { |
949 | return nil, fmt.Errorf("Not found: %s", name) | 1153 | return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path) |
950 | } | 1154 | } |
951 | 1155 | ||
952 | is := rs.Primary | 1156 | is := rs.Primary |
953 | if is == nil { | 1157 | if is == nil { |
954 | return nil, fmt.Errorf("No primary instance: %s", name) | 1158 | return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path) |
955 | } | 1159 | } |
956 | 1160 | ||
957 | return is, nil | 1161 | return is, nil |
958 | } | 1162 | } |
1163 | |||
1164 | // modulePathPrimaryInstanceState returns the primary instance state for the | ||
1165 | // given resource name in a given module path. | ||
1166 | func modulePathPrimaryInstanceState(s *terraform.State, mp []string, name string) (*terraform.InstanceState, error) { | ||
1167 | ms := s.ModuleByPath(mp) | ||
1168 | if ms == nil { | ||
1169 | return nil, fmt.Errorf("No module found at: %s", mp) | ||
1170 | } | ||
1171 | |||
1172 | return modulePrimaryInstanceState(s, ms, name) | ||
1173 | } | ||
1174 | |||
1175 | // primaryInstanceState returns the primary instance state for the given | ||
1176 | // resource name in the root module. | ||
1177 | func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) { | ||
1178 | ms := s.RootModule() | ||
1179 | return modulePrimaryInstanceState(s, ms, name) | ||
1180 | } | ||