]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/hashicorp/terraform/helper/resource/testing.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / hashicorp / terraform / helper / resource / testing.go
1 package resource
2
3 import (
4 "bytes"
5 "flag"
6 "fmt"
7 "io"
8 "io/ioutil"
9 "log"
10 "os"
11 "path/filepath"
12 "reflect"
13 "regexp"
14 "strings"
15 "syscall"
16 "testing"
17
18 "github.com/davecgh/go-spew/spew"
19 "github.com/hashicorp/errwrap"
20 "github.com/hashicorp/go-multierror"
21 "github.com/hashicorp/logutils"
22 "github.com/mitchellh/colorstring"
23
24 "github.com/hashicorp/terraform/addrs"
25 "github.com/hashicorp/terraform/command/format"
26 "github.com/hashicorp/terraform/configs"
27 "github.com/hashicorp/terraform/configs/configload"
28 "github.com/hashicorp/terraform/helper/logging"
29 "github.com/hashicorp/terraform/internal/initwd"
30 "github.com/hashicorp/terraform/providers"
31 "github.com/hashicorp/terraform/states"
32 "github.com/hashicorp/terraform/terraform"
33 "github.com/hashicorp/terraform/tfdiags"
34 )
35
36 // flagSweep is a flag available when running tests on the command line. It
37 // contains a comma seperated list of regions to for the sweeper functions to
38 // run in. This flag bypasses the normal Test path and instead runs functions designed to
39 // clean up any leaked resources a testing environment could have created. It is
40 // a best effort attempt, and relies on Provider authors to implement "Sweeper"
41 // methods for resources.
42
43 // Adding Sweeper methods with AddTestSweepers will
44 // construct a list of sweeper funcs to be called here. We iterate through
45 // regions provided by the sweep flag, and for each region we iterate through the
46 // tests, and exit on any errors. At time of writing, sweepers are ran
47 // sequentially, however they can list dependencies to be ran first. We track
48 // the sweepers that have been ran, so as to not run a sweeper twice for a given
49 // region.
50 //
51 // WARNING:
52 // Sweepers are designed to be destructive. You should not use the -sweep flag
53 // in any environment that is not strictly a test environment. Resources will be
54 // destroyed.
55
56 var flagSweep = flag.String("sweep", "", "List of Regions to run available Sweepers")
57 var flagSweepRun = flag.String("sweep-run", "", "Comma seperated list of Sweeper Tests to run")
58 var sweeperFuncs map[string]*Sweeper
59
60 // map of sweepers that have ran, and the success/fail status based on any error
61 // raised
62 var sweeperRunList map[string]bool
63
64 // type SweeperFunc is a signature for a function that acts as a sweeper. It
65 // accepts a string for the region that the sweeper is to be ran in. This
66 // function must be able to construct a valid client for that region.
67 type SweeperFunc func(r string) error
68
69 type Sweeper struct {
70 // Name for sweeper. Must be unique to be ran by the Sweeper Runner
71 Name string
72
73 // Dependencies list the const names of other Sweeper functions that must be ran
74 // prior to running this Sweeper. This is an ordered list that will be invoked
75 // recursively at the helper/resource level
76 Dependencies []string
77
78 // Sweeper function that when invoked sweeps the Provider of specific
79 // resources
80 F SweeperFunc
81 }
82
83 func init() {
84 sweeperFuncs = make(map[string]*Sweeper)
85 }
86
87 // AddTestSweepers function adds a given name and Sweeper configuration
88 // pair to the internal sweeperFuncs map. Invoke this function to register a
89 // resource sweeper to be available for running when the -sweep flag is used
90 // with `go test`. Sweeper names must be unique to help ensure a given sweeper
91 // is only ran once per run.
92 func AddTestSweepers(name string, s *Sweeper) {
93 if _, ok := sweeperFuncs[name]; ok {
94 log.Fatalf("[ERR] Error adding (%s) to sweeperFuncs: function already exists in map", name)
95 }
96
97 sweeperFuncs[name] = s
98 }
99
100 func TestMain(m *testing.M) {
101 flag.Parse()
102 if *flagSweep != "" {
103 // parse flagSweep contents for regions to run
104 regions := strings.Split(*flagSweep, ",")
105
106 // get filtered list of sweepers to run based on sweep-run flag
107 sweepers := filterSweepers(*flagSweepRun, sweeperFuncs)
108 for _, region := range regions {
109 region = strings.TrimSpace(region)
110 // reset sweeperRunList for each region
111 sweeperRunList = map[string]bool{}
112
113 log.Printf("[DEBUG] Running Sweepers for region (%s):\n", region)
114 for _, sweeper := range sweepers {
115 if err := runSweeperWithRegion(region, sweeper); err != nil {
116 log.Fatalf("[ERR] error running (%s): %s", sweeper.Name, err)
117 }
118 }
119
120 log.Printf("Sweeper Tests ran:\n")
121 for s, _ := range sweeperRunList {
122 fmt.Printf("\t- %s\n", s)
123 }
124 }
125 } else {
126 os.Exit(m.Run())
127 }
128 }
129
130 // filterSweepers takes a comma seperated string listing the names of sweepers
131 // to be ran, and returns a filtered set from the list of all of sweepers to
132 // run based on the names given.
133 func filterSweepers(f string, source map[string]*Sweeper) map[string]*Sweeper {
134 filterSlice := strings.Split(strings.ToLower(f), ",")
135 if len(filterSlice) == 1 && filterSlice[0] == "" {
136 // if the filter slice is a single element of "" then no sweeper list was
137 // given, so just return the full list
138 return source
139 }
140
141 sweepers := make(map[string]*Sweeper)
142 for name, sweeper := range source {
143 for _, s := range filterSlice {
144 if strings.Contains(strings.ToLower(name), s) {
145 sweepers[name] = sweeper
146 }
147 }
148 }
149 return sweepers
150 }
151
152 // runSweeperWithRegion recieves a sweeper and a region, and recursively calls
153 // itself with that region for every dependency found for that sweeper. If there
154 // are no dependencies, invoke the contained sweeper fun with the region, and
155 // add the success/fail status to the sweeperRunList.
156 func runSweeperWithRegion(region string, s *Sweeper) error {
157 for _, dep := range s.Dependencies {
158 if depSweeper, ok := sweeperFuncs[dep]; ok {
159 log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), running..", s.Name, dep)
160 if err := runSweeperWithRegion(region, depSweeper); err != nil {
161 return err
162 }
163 } else {
164 log.Printf("[DEBUG] Sweeper (%s) has dependency (%s), but that sweeper was not found", s.Name, dep)
165 }
166 }
167
168 if _, ok := sweeperRunList[s.Name]; ok {
169 log.Printf("[DEBUG] Sweeper (%s) already ran in region (%s)", s.Name, region)
170 return nil
171 }
172
173 runE := s.F(region)
174 if runE == nil {
175 sweeperRunList[s.Name] = true
176 } else {
177 sweeperRunList[s.Name] = false
178 }
179
180 return runE
181 }
182
183 const TestEnvVar = "TF_ACC"
184
185 // TestProvider can be implemented by any ResourceProvider to provide custom
186 // reset functionality at the start of an acceptance test.
187 // The helper/schema Provider implements this interface.
188 type TestProvider interface {
189 TestReset() error
190 }
191
192 // TestCheckFunc is the callback type used with acceptance tests to check
193 // the state of a resource. The state passed in is the latest state known,
194 // or in the case of being after a destroy, it is the last known state when
195 // it was created.
196 type TestCheckFunc func(*terraform.State) error
197
198 // ImportStateCheckFunc is the check function for ImportState tests
199 type ImportStateCheckFunc func([]*terraform.InstanceState) error
200
201 // ImportStateIdFunc is an ID generation function to help with complex ID
202 // generation for ImportState tests.
203 type ImportStateIdFunc func(*terraform.State) (string, error)
204
205 // TestCase is a single acceptance test case used to test the apply/destroy
206 // lifecycle of a resource in a specific configuration.
207 //
208 // When the destroy plan is executed, the config from the last TestStep
209 // is used to plan it.
210 type TestCase struct {
211 // IsUnitTest allows a test to run regardless of the TF_ACC
212 // environment variable. This should be used with care - only for
213 // fast tests on local resources (e.g. remote state with a local
214 // backend) but can be used to increase confidence in correct
215 // operation of Terraform without waiting for a full acctest run.
216 IsUnitTest bool
217
218 // PreCheck, if non-nil, will be called before any test steps are
219 // executed. It will only be executed in the case that the steps
220 // would run, so it can be used for some validation before running
221 // acceptance tests, such as verifying that keys are setup.
222 PreCheck func()
223
224 // Providers is the ResourceProvider that will be under test.
225 //
226 // Alternately, ProviderFactories can be specified for the providers
227 // that are valid. This takes priority over Providers.
228 //
229 // The end effect of each is the same: specifying the providers that
230 // are used within the tests.
231 Providers map[string]terraform.ResourceProvider
232 ProviderFactories map[string]terraform.ResourceProviderFactory
233
234 // PreventPostDestroyRefresh can be set to true for cases where data sources
235 // are tested alongside real resources
236 PreventPostDestroyRefresh bool
237
238 // CheckDestroy is called after the resource is finally destroyed
239 // to allow the tester to test that the resource is truly gone.
240 CheckDestroy TestCheckFunc
241
242 // Steps are the apply sequences done within the context of the
243 // same state. Each step can have its own check to verify correctness.
244 Steps []TestStep
245
246 // The settings below control the "ID-only refresh test." This is
247 // an enabled-by-default test that tests that a refresh can be
248 // refreshed with only an ID to result in the same attributes.
249 // This validates completeness of Refresh.
250 //
251 // IDRefreshName is the name of the resource to check. This will
252 // default to the first non-nil primary resource in the state.
253 //
254 // IDRefreshIgnore is a list of configuration keys that will be ignored.
255 IDRefreshName string
256 IDRefreshIgnore []string
257 }
258
259 // TestStep is a single apply sequence of a test, done within the
260 // context of a state.
261 //
262 // Multiple TestSteps can be sequenced in a Test to allow testing
263 // potentially complex update logic. In general, simply create/destroy
264 // tests will only need one step.
265 type TestStep struct {
266 // ResourceName should be set to the name of the resource
267 // that is being tested. Example: "aws_instance.foo". Various test
268 // modes use this to auto-detect state information.
269 //
270 // This is only required if the test mode settings below say it is
271 // for the mode you're using.
272 ResourceName string
273
274 // PreConfig is called before the Config is applied to perform any per-step
275 // setup that needs to happen. This is called regardless of "test mode"
276 // below.
277 PreConfig func()
278
279 // Taint is a list of resource addresses to taint prior to the execution of
280 // the step. Be sure to only include this at a step where the referenced
281 // address will be present in state, as it will fail the test if the resource
282 // is missing.
283 //
284 // This option is ignored on ImportState tests, and currently only works for
285 // resources in the root module path.
286 Taint []string
287
288 //---------------------------------------------------------------
289 // Test modes. One of the following groups of settings must be
290 // set to determine what the test step will do. Ideally we would've
291 // used Go interfaces here but there are now hundreds of tests we don't
292 // want to re-type so instead we just determine which step logic
293 // to run based on what settings below are set.
294 //---------------------------------------------------------------
295
296 //---------------------------------------------------------------
297 // Plan, Apply testing
298 //---------------------------------------------------------------
299
300 // Config a string of the configuration to give to Terraform. If this
301 // is set, then the TestCase will execute this step with the same logic
302 // as a `terraform apply`.
303 Config string
304
305 // Check is called after the Config is applied. Use this step to
306 // make your own API calls to check the status of things, and to
307 // inspect the format of the ResourceState itself.
308 //
309 // If an error is returned, the test will fail. In this case, a
310 // destroy plan will still be attempted.
311 //
312 // If this is nil, no check is done on this step.
313 Check TestCheckFunc
314
315 // Destroy will create a destroy plan if set to true.
316 Destroy bool
317
318 // ExpectNonEmptyPlan can be set to true for specific types of tests that are
319 // looking to verify that a diff occurs
320 ExpectNonEmptyPlan bool
321
322 // ExpectError allows the construction of test cases that we expect to fail
323 // with an error. The specified regexp must match against the error for the
324 // test to pass.
325 ExpectError *regexp.Regexp
326
327 // PlanOnly can be set to only run `plan` with this configuration, and not
328 // actually apply it. This is useful for ensuring config changes result in
329 // no-op plans
330 PlanOnly bool
331
332 // PreventDiskCleanup can be set to true for testing terraform modules which
333 // require access to disk at runtime. Note that this will leave files in the
334 // temp folder
335 PreventDiskCleanup bool
336
337 // PreventPostDestroyRefresh can be set to true for cases where data sources
338 // are tested alongside real resources
339 PreventPostDestroyRefresh bool
340
341 // SkipFunc is called before applying config, but after PreConfig
342 // This is useful for defining test steps with platform-dependent checks
343 SkipFunc func() (bool, error)
344
345 //---------------------------------------------------------------
346 // ImportState testing
347 //---------------------------------------------------------------
348
349 // ImportState, if true, will test the functionality of ImportState
350 // by importing the resource with ResourceName (must be set) and the
351 // ID of that resource.
352 ImportState bool
353
354 // ImportStateId is the ID to perform an ImportState operation with.
355 // This is optional. If it isn't set, then the resource ID is automatically
356 // determined by inspecting the state for ResourceName's ID.
357 ImportStateId string
358
359 // ImportStateIdPrefix is the prefix added in front of ImportStateId.
360 // This can be useful in complex import cases, where more than one
361 // attribute needs to be passed on as the Import ID. Mainly in cases
362 // where the ID is not known, and a known prefix needs to be added to
363 // the unset ImportStateId field.
364 ImportStateIdPrefix string
365
366 // ImportStateIdFunc is a function that can be used to dynamically generate
367 // the ID for the ImportState tests. It is sent the state, which can be
368 // checked to derive the attributes necessary and generate the string in the
369 // desired format.
370 ImportStateIdFunc ImportStateIdFunc
371
372 // ImportStateCheck checks the results of ImportState. It should be
373 // used to verify that the resulting value of ImportState has the
374 // proper resources, IDs, and attributes.
375 ImportStateCheck ImportStateCheckFunc
376
377 // ImportStateVerify, if true, will also check that the state values
378 // that are finally put into the state after import match for all the
379 // IDs returned by the Import.
380 //
381 // ImportStateVerifyIgnore are fields that should not be verified to
382 // be equal. These can be set to ephemeral fields or fields that can't
383 // be refreshed and don't matter.
384 ImportStateVerify bool
385 ImportStateVerifyIgnore []string
386
387 // provider s is used internally to maintain a reference to the
388 // underlying providers during the tests
389 providers map[string]terraform.ResourceProvider
390 }
391
392 // Set to a file mask in sprintf format where %s is test name
393 const EnvLogPathMask = "TF_LOG_PATH_MASK"
394
395 func LogOutput(t TestT) (logOutput io.Writer, err error) {
396 logOutput = ioutil.Discard
397
398 logLevel := logging.LogLevel()
399 if logLevel == "" {
400 return
401 }
402
403 logOutput = os.Stderr
404
405 if logPath := os.Getenv(logging.EnvLogFile); logPath != "" {
406 var err error
407 logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
408 if err != nil {
409 return nil, err
410 }
411 }
412
413 if logPathMask := os.Getenv(EnvLogPathMask); logPathMask != "" {
414 // Escape special characters which may appear if we have subtests
415 testName := strings.Replace(t.Name(), "/", "__", -1)
416
417 logPath := fmt.Sprintf(logPathMask, testName)
418 var err error
419 logOutput, err = os.OpenFile(logPath, syscall.O_CREAT|syscall.O_RDWR|syscall.O_APPEND, 0666)
420 if err != nil {
421 return nil, err
422 }
423 }
424
425 // This was the default since the beginning
426 logOutput = &logutils.LevelFilter{
427 Levels: logging.ValidLevels,
428 MinLevel: logutils.LogLevel(logLevel),
429 Writer: logOutput,
430 }
431
432 return
433 }
434
435 // ParallelTest performs an acceptance test on a resource, allowing concurrency
436 // with other ParallelTest.
437 //
438 // Tests will fail if they do not properly handle conditions to allow multiple
439 // tests to occur against the same resource or service (e.g. random naming).
440 // All other requirements of the Test function also apply to this function.
441 func ParallelTest(t TestT, c TestCase) {
442 t.Parallel()
443 Test(t, c)
444 }
445
446 // Test performs an acceptance test on a resource.
447 //
448 // Tests are not run unless an environmental variable "TF_ACC" is
449 // set to some non-empty value. This is to avoid test cases surprising
450 // a user by creating real resources.
451 //
452 // Tests will fail unless the verbose flag (`go test -v`, or explicitly
453 // the "-test.v" flag) is set. Because some acceptance tests take quite
454 // long, we require the verbose flag so users are able to see progress
455 // output.
456 func Test(t TestT, c TestCase) {
457 // We only run acceptance tests if an env var is set because they're
458 // slow and generally require some outside configuration. You can opt out
459 // of this with OverrideEnvVar on individual TestCases.
460 if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest {
461 t.Skip(fmt.Sprintf(
462 "Acceptance tests skipped unless env '%s' set",
463 TestEnvVar))
464 return
465 }
466
467 logWriter, err := LogOutput(t)
468 if err != nil {
469 t.Error(fmt.Errorf("error setting up logging: %s", err))
470 }
471 log.SetOutput(logWriter)
472
473 // We require verbose mode so that the user knows what is going on.
474 if !testTesting && !testing.Verbose() && !c.IsUnitTest {
475 t.Fatal("Acceptance tests must be run with the -v flag on tests")
476 return
477 }
478
479 // Run the PreCheck if we have it
480 if c.PreCheck != nil {
481 c.PreCheck()
482 }
483
484 // get instances of all providers, so we can use the individual
485 // resources to shim the state during the tests.
486 providers := make(map[string]terraform.ResourceProvider)
487 for name, pf := range testProviderFactories(c) {
488 p, err := pf()
489 if err != nil {
490 t.Fatal(err)
491 }
492 providers[name] = p
493 }
494
495 providerResolver, err := testProviderResolver(c)
496 if err != nil {
497 t.Fatal(err)
498 }
499
500 opts := terraform.ContextOpts{ProviderResolver: providerResolver}
501
502 // A single state variable to track the lifecycle, starting with no state
503 var state *terraform.State
504
505 // Go through each step and run it
506 var idRefreshCheck *terraform.ResourceState
507 idRefresh := c.IDRefreshName != ""
508 errored := false
509 for i, step := range c.Steps {
510 // insert the providers into the step so we can get the resources for
511 // shimming the state
512 step.providers = providers
513
514 var err error
515 log.Printf("[DEBUG] Test: Executing step %d", i)
516
517 if step.SkipFunc != nil {
518 skip, err := step.SkipFunc()
519 if err != nil {
520 t.Fatal(err)
521 }
522 if skip {
523 log.Printf("[WARN] Skipping step %d", i)
524 continue
525 }
526 }
527
528 if step.Config == "" && !step.ImportState {
529 err = fmt.Errorf(
530 "unknown test mode for step. Please see TestStep docs\n\n%#v",
531 step)
532 } else {
533 if step.ImportState {
534 if step.Config == "" {
535 step.Config = testProviderConfig(c)
536 }
537
538 // Can optionally set step.Config in addition to
539 // step.ImportState, to provide config for the import.
540 state, err = testStepImportState(opts, state, step)
541 } else {
542 state, err = testStepConfig(opts, state, step)
543 }
544 }
545
546 // If we expected an error, but did not get one, fail
547 if err == nil && step.ExpectError != nil {
548 errored = true
549 t.Error(fmt.Sprintf(
550 "Step %d, no error received, but expected a match to:\n\n%s\n\n",
551 i, step.ExpectError))
552 break
553 }
554
555 // If there was an error, exit
556 if err != nil {
557 // Perhaps we expected an error? Check if it matches
558 if step.ExpectError != nil {
559 if !step.ExpectError.MatchString(err.Error()) {
560 errored = true
561 t.Error(fmt.Sprintf(
562 "Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n",
563 i, err, step.ExpectError))
564 break
565 }
566 } else {
567 errored = true
568 t.Error(fmt.Sprintf("Step %d error: %s", i, detailedErrorMessage(err)))
569 break
570 }
571 }
572
573 // If we've never checked an id-only refresh and our state isn't
574 // empty, find the first resource and test it.
575 if idRefresh && idRefreshCheck == nil && !state.Empty() {
576 // Find the first non-nil resource in the state
577 for _, m := range state.Modules {
578 if len(m.Resources) > 0 {
579 if v, ok := m.Resources[c.IDRefreshName]; ok {
580 idRefreshCheck = v
581 }
582
583 break
584 }
585 }
586
587 // If we have an instance to check for refreshes, do it
588 // immediately. We do it in the middle of another test
589 // because it shouldn't affect the overall state (refresh
590 // is read-only semantically) and we want to fail early if
591 // this fails. If refresh isn't read-only, then this will have
592 // caught a different bug.
593 if idRefreshCheck != nil {
594 log.Printf(
595 "[WARN] Test: Running ID-only refresh check on %s",
596 idRefreshCheck.Primary.ID)
597 if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil {
598 log.Printf("[ERROR] Test: ID-only test failed: %s", err)
599 t.Error(fmt.Sprintf(
600 "[ERROR] Test: ID-only test failed: %s", err))
601 break
602 }
603 }
604 }
605 }
606
607 // If we never checked an id-only refresh, it is a failure.
608 if idRefresh {
609 if !errored && len(c.Steps) > 0 && idRefreshCheck == nil {
610 t.Error("ID-only refresh check never ran.")
611 }
612 }
613
614 // If we have a state, then run the destroy
615 if state != nil {
616 lastStep := c.Steps[len(c.Steps)-1]
617 destroyStep := TestStep{
618 Config: lastStep.Config,
619 Check: c.CheckDestroy,
620 Destroy: true,
621 PreventDiskCleanup: lastStep.PreventDiskCleanup,
622 PreventPostDestroyRefresh: c.PreventPostDestroyRefresh,
623 providers: providers,
624 }
625
626 log.Printf("[WARN] Test: Executing destroy step")
627 state, err := testStep(opts, state, destroyStep)
628 if err != nil {
629 t.Error(fmt.Sprintf(
630 "Error destroying resource! WARNING: Dangling resources\n"+
631 "may exist. The full state and error is shown below.\n\n"+
632 "Error: %s\n\nState: %s",
633 err,
634 state))
635 }
636 } else {
637 log.Printf("[WARN] Skipping destroy test since there is no state.")
638 }
639 }
640
641 // testProviderConfig takes the list of Providers in a TestCase and returns a
642 // config with only empty provider blocks. This is useful for Import, where no
643 // config is provided, but the providers must be defined.
644 func testProviderConfig(c TestCase) string {
645 var lines []string
646 for p := range c.Providers {
647 lines = append(lines, fmt.Sprintf("provider %q {}\n", p))
648 }
649
650 return strings.Join(lines, "")
651 }
652
653 // testProviderFactories combines the fixed Providers and
654 // ResourceProviderFactory functions into a single map of
655 // ResourceProviderFactory functions.
656 func testProviderFactories(c TestCase) map[string]terraform.ResourceProviderFactory {
657 ctxProviders := make(map[string]terraform.ResourceProviderFactory)
658 for k, pf := range c.ProviderFactories {
659 ctxProviders[k] = pf
660 }
661
662 // add any fixed providers
663 for k, p := range c.Providers {
664 ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
665 }
666 return ctxProviders
667 }
668
669 // testProviderResolver is a helper to build a ResourceProviderResolver
670 // with pre instantiated ResourceProviders, so that we can reset them for the
671 // test, while only calling the factory function once.
672 // Any errors are stored so that they can be returned by the factory in
673 // terraform to match non-test behavior.
674 func testProviderResolver(c TestCase) (providers.Resolver, error) {
675 ctxProviders := testProviderFactories(c)
676
677 // wrap the old provider factories in the test grpc server so they can be
678 // called from terraform.
679 newProviders := make(map[string]providers.Factory)
680
681 for k, pf := range ctxProviders {
682 factory := pf // must copy to ensure each closure sees its own value
683 newProviders[k] = func() (providers.Interface, error) {
684 p, err := factory()
685 if err != nil {
686 return nil, err
687 }
688
689 // The provider is wrapped in a GRPCTestProvider so that it can be
690 // passed back to terraform core as a providers.Interface, rather
691 // than the legacy ResourceProvider.
692 return GRPCTestProvider(p), nil
693 }
694 }
695
696 return providers.ResolverFixed(newProviders), nil
697 }
698
699 // UnitTest is a helper to force the acceptance testing harness to run in the
700 // normal unit test suite. This should only be used for resource that don't
701 // have any external dependencies.
702 func UnitTest(t TestT, c TestCase) {
703 c.IsUnitTest = true
704 Test(t, c)
705 }
706
707 func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error {
708 // TODO: We guard by this right now so master doesn't explode. We
709 // need to remove this eventually to make this part of the normal tests.
710 if os.Getenv("TF_ACC_IDONLY") == "" {
711 return nil
712 }
713
714 addr := addrs.Resource{
715 Mode: addrs.ManagedResourceMode,
716 Type: r.Type,
717 Name: "foo",
718 }.Instance(addrs.NoKey)
719 absAddr := addr.Absolute(addrs.RootModuleInstance)
720
721 // Build the state. The state is just the resource with an ID. There
722 // are no attributes. We only set what is needed to perform a refresh.
723 state := states.NewState()
724 state.RootModule().SetResourceInstanceCurrent(
725 addr,
726 &states.ResourceInstanceObjectSrc{
727 AttrsFlat: r.Primary.Attributes,
728 Status: states.ObjectReady,
729 },
730 addrs.ProviderConfig{Type: "placeholder"}.Absolute(addrs.RootModuleInstance),
731 )
732
733 // Create the config module. We use the full config because Refresh
734 // doesn't have access to it and we may need things like provider
735 // configurations. The initial implementation of id-only checks used
736 // an empty config module, but that caused the aforementioned problems.
737 cfg, err := testConfig(opts, step)
738 if err != nil {
739 return err
740 }
741
742 // Initialize the context
743 opts.Config = cfg
744 opts.State = state
745 ctx, ctxDiags := terraform.NewContext(&opts)
746 if ctxDiags.HasErrors() {
747 return ctxDiags.Err()
748 }
749 if diags := ctx.Validate(); len(diags) > 0 {
750 if diags.HasErrors() {
751 return errwrap.Wrapf("config is invalid: {{err}}", diags.Err())
752 }
753
754 log.Printf("[WARN] Config warnings:\n%s", diags.Err().Error())
755 }
756
757 // Refresh!
758 state, refreshDiags := ctx.Refresh()
759 if refreshDiags.HasErrors() {
760 return refreshDiags.Err()
761 }
762
763 // Verify attribute equivalence.
764 actualR := state.ResourceInstance(absAddr)
765 if actualR == nil {
766 return fmt.Errorf("Resource gone!")
767 }
768 if actualR.Current == nil {
769 return fmt.Errorf("Resource has no primary instance")
770 }
771 actual := actualR.Current.AttrsFlat
772 expected := r.Primary.Attributes
773 // Remove fields we're ignoring
774 for _, v := range c.IDRefreshIgnore {
775 for k, _ := range actual {
776 if strings.HasPrefix(k, v) {
777 delete(actual, k)
778 }
779 }
780 for k, _ := range expected {
781 if strings.HasPrefix(k, v) {
782 delete(expected, k)
783 }
784 }
785 }
786
787 if !reflect.DeepEqual(actual, expected) {
788 // Determine only the different attributes
789 for k, v := range expected {
790 if av, ok := actual[k]; ok && v == av {
791 delete(expected, k)
792 delete(actual, k)
793 }
794 }
795
796 spewConf := spew.NewDefaultConfig()
797 spewConf.SortKeys = true
798 return fmt.Errorf(
799 "Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
800 "\n\n%s\n\n%s",
801 spewConf.Sdump(actual), spewConf.Sdump(expected))
802 }
803
804 return nil
805 }
806
807 func testConfig(opts terraform.ContextOpts, step TestStep) (*configs.Config, error) {
808 if step.PreConfig != nil {
809 step.PreConfig()
810 }
811
812 cfgPath, err := ioutil.TempDir("", "tf-test")
813 if err != nil {
814 return nil, fmt.Errorf("Error creating temporary directory for config: %s", err)
815 }
816
817 if step.PreventDiskCleanup {
818 log.Printf("[INFO] Skipping defer os.RemoveAll call")
819 } else {
820 defer os.RemoveAll(cfgPath)
821 }
822
823 // Write the main configuration file
824 err = ioutil.WriteFile(filepath.Join(cfgPath, "main.tf"), []byte(step.Config), os.ModePerm)
825 if err != nil {
826 return nil, fmt.Errorf("Error creating temporary file for config: %s", err)
827 }
828
829 // Create directory for our child modules, if any.
830 modulesDir := filepath.Join(cfgPath, ".modules")
831 err = os.Mkdir(modulesDir, os.ModePerm)
832 if err != nil {
833 return nil, fmt.Errorf("Error creating child modules directory: %s", err)
834 }
835
836 inst := initwd.NewModuleInstaller(modulesDir, nil)
837 _, installDiags := inst.InstallModules(cfgPath, true, initwd.ModuleInstallHooksImpl{})
838 if installDiags.HasErrors() {
839 return nil, installDiags.Err()
840 }
841
842 loader, err := configload.NewLoader(&configload.Config{
843 ModulesDir: modulesDir,
844 })
845 if err != nil {
846 return nil, fmt.Errorf("failed to create config loader: %s", err)
847 }
848
849 config, configDiags := loader.LoadConfig(cfgPath)
850 if configDiags.HasErrors() {
851 return nil, configDiags
852 }
853
854 return config, nil
855 }
856
857 func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) {
858 if c.ResourceName == "" {
859 return nil, fmt.Errorf("ResourceName must be set in TestStep")
860 }
861
862 for _, m := range state.Modules {
863 if len(m.Resources) > 0 {
864 if v, ok := m.Resources[c.ResourceName]; ok {
865 return v, nil
866 }
867 }
868 }
869
870 return nil, fmt.Errorf(
871 "Resource specified by ResourceName couldn't be found: %s", c.ResourceName)
872 }
873
874 // ComposeTestCheckFunc lets you compose multiple TestCheckFuncs into
875 // a single TestCheckFunc.
876 //
877 // As a user testing their provider, this lets you decompose your checks
878 // into smaller pieces more easily.
879 func ComposeTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc {
880 return func(s *terraform.State) error {
881 for i, f := range fs {
882 if err := f(s); err != nil {
883 return fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err)
884 }
885 }
886
887 return nil
888 }
889 }
890
891 // ComposeAggregateTestCheckFunc lets you compose multiple TestCheckFuncs into
892 // a single TestCheckFunc.
893 //
894 // As a user testing their provider, this lets you decompose your checks
895 // into smaller pieces more easily.
896 //
897 // Unlike ComposeTestCheckFunc, ComposeAggergateTestCheckFunc runs _all_ of the
898 // TestCheckFuncs and aggregates failures.
899 func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc {
900 return func(s *terraform.State) error {
901 var result *multierror.Error
902
903 for i, f := range fs {
904 if err := f(s); err != nil {
905 result = multierror.Append(result, fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err))
906 }
907 }
908
909 return result.ErrorOrNil()
910 }
911 }
912
913 // TestCheckResourceAttrSet is a TestCheckFunc which ensures a value
914 // exists in state for the given name/key combination. It is useful when
915 // testing that computed values were set, when it is not possible to
916 // know ahead of time what the values will be.
917 func TestCheckResourceAttrSet(name, key string) TestCheckFunc {
918 return func(s *terraform.State) error {
919 is, err := primaryInstanceState(s, name)
920 if err != nil {
921 return err
922 }
923
924 return testCheckResourceAttrSet(is, name, key)
925 }
926 }
927
928 // TestCheckModuleResourceAttrSet - as per TestCheckResourceAttrSet but with
929 // support for non-root modules
930 func TestCheckModuleResourceAttrSet(mp []string, name string, key string) TestCheckFunc {
931 mpt := addrs.Module(mp).UnkeyedInstanceShim()
932 return func(s *terraform.State) error {
933 is, err := modulePathPrimaryInstanceState(s, mpt, name)
934 if err != nil {
935 return err
936 }
937
938 return testCheckResourceAttrSet(is, name, key)
939 }
940 }
941
942 func testCheckResourceAttrSet(is *terraform.InstanceState, name string, key string) error {
943 if val, ok := is.Attributes[key]; !ok || val == "" {
944 return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key)
945 }
946
947 return nil
948 }
949
950 // TestCheckResourceAttr is a TestCheckFunc which validates
951 // the value in state for the given name/key combination.
952 func TestCheckResourceAttr(name, key, value string) TestCheckFunc {
953 return func(s *terraform.State) error {
954 is, err := primaryInstanceState(s, name)
955 if err != nil {
956 return err
957 }
958
959 return testCheckResourceAttr(is, name, key, value)
960 }
961 }
962
963 // TestCheckModuleResourceAttr - as per TestCheckResourceAttr but with
964 // support for non-root modules
965 func TestCheckModuleResourceAttr(mp []string, name string, key string, value string) TestCheckFunc {
966 mpt := addrs.Module(mp).UnkeyedInstanceShim()
967 return func(s *terraform.State) error {
968 is, err := modulePathPrimaryInstanceState(s, mpt, name)
969 if err != nil {
970 return err
971 }
972
973 return testCheckResourceAttr(is, name, key, value)
974 }
975 }
976
977 func testCheckResourceAttr(is *terraform.InstanceState, name string, key string, value string) error {
978 // Empty containers may be elided from the state.
979 // If the intent here is to check for an empty container, allow the key to
980 // also be non-existent.
981 emptyCheck := false
982 if value == "0" && (strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%")) {
983 emptyCheck = true
984 }
985
986 if v, ok := is.Attributes[key]; !ok || v != value {
987 if emptyCheck && !ok {
988 return nil
989 }
990
991 if !ok {
992 return fmt.Errorf("%s: Attribute '%s' not found", name, key)
993 }
994
995 return fmt.Errorf(
996 "%s: Attribute '%s' expected %#v, got %#v",
997 name,
998 key,
999 value,
1000 v)
1001 }
1002 return nil
1003 }
1004
1005 // TestCheckNoResourceAttr is a TestCheckFunc which ensures that
1006 // NO value exists in state for the given name/key combination.
1007 func TestCheckNoResourceAttr(name, key string) TestCheckFunc {
1008 return func(s *terraform.State) error {
1009 is, err := primaryInstanceState(s, name)
1010 if err != nil {
1011 return err
1012 }
1013
1014 return testCheckNoResourceAttr(is, name, key)
1015 }
1016 }
1017
1018 // TestCheckModuleNoResourceAttr - as per TestCheckNoResourceAttr but with
1019 // support for non-root modules
1020 func TestCheckModuleNoResourceAttr(mp []string, name string, key string) TestCheckFunc {
1021 mpt := addrs.Module(mp).UnkeyedInstanceShim()
1022 return func(s *terraform.State) error {
1023 is, err := modulePathPrimaryInstanceState(s, mpt, name)
1024 if err != nil {
1025 return err
1026 }
1027
1028 return testCheckNoResourceAttr(is, name, key)
1029 }
1030 }
1031
1032 func testCheckNoResourceAttr(is *terraform.InstanceState, name string, key string) error {
1033 // Empty containers may sometimes be included in the state.
1034 // If the intent here is to check for an empty container, allow the value to
1035 // also be "0".
1036 emptyCheck := false
1037 if strings.HasSuffix(key, ".#") || strings.HasSuffix(key, ".%") {
1038 emptyCheck = true
1039 }
1040
1041 val, exists := is.Attributes[key]
1042 if emptyCheck && val == "0" {
1043 return nil
1044 }
1045
1046 if exists {
1047 return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key)
1048 }
1049
1050 return nil
1051 }
1052
1053 // TestMatchResourceAttr is a TestCheckFunc which checks that the value
1054 // in state for the given name/key combination matches the given regex.
1055 func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc {
1056 return func(s *terraform.State) error {
1057 is, err := primaryInstanceState(s, name)
1058 if err != nil {
1059 return err
1060 }
1061
1062 return testMatchResourceAttr(is, name, key, r)
1063 }
1064 }
1065
1066 // TestModuleMatchResourceAttr - as per TestMatchResourceAttr but with
1067 // support for non-root modules
1068 func TestModuleMatchResourceAttr(mp []string, name string, key string, r *regexp.Regexp) TestCheckFunc {
1069 mpt := addrs.Module(mp).UnkeyedInstanceShim()
1070 return func(s *terraform.State) error {
1071 is, err := modulePathPrimaryInstanceState(s, mpt, name)
1072 if err != nil {
1073 return err
1074 }
1075
1076 return testMatchResourceAttr(is, name, key, r)
1077 }
1078 }
1079
1080 func testMatchResourceAttr(is *terraform.InstanceState, name string, key string, r *regexp.Regexp) error {
1081 if !r.MatchString(is.Attributes[key]) {
1082 return fmt.Errorf(
1083 "%s: Attribute '%s' didn't match %q, got %#v",
1084 name,
1085 key,
1086 r.String(),
1087 is.Attributes[key])
1088 }
1089
1090 return nil
1091 }
1092
1093 // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the
1094 // value is a pointer so that it can be updated while the test is running.
1095 // It will only be dereferenced at the point this step is run.
1096 func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckFunc {
1097 return func(s *terraform.State) error {
1098 return TestCheckResourceAttr(name, key, *value)(s)
1099 }
1100 }
1101
1102 // TestCheckModuleResourceAttrPtr - as per TestCheckResourceAttrPtr but with
1103 // support for non-root modules
1104 func TestCheckModuleResourceAttrPtr(mp []string, name string, key string, value *string) TestCheckFunc {
1105 return func(s *terraform.State) error {
1106 return TestCheckModuleResourceAttr(mp, name, key, *value)(s)
1107 }
1108 }
1109
1110 // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values
1111 // in state for a pair of name/key combinations are equal.
1112 func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc {
1113 return func(s *terraform.State) error {
1114 isFirst, err := primaryInstanceState(s, nameFirst)
1115 if err != nil {
1116 return err
1117 }
1118
1119 isSecond, err := primaryInstanceState(s, nameSecond)
1120 if err != nil {
1121 return err
1122 }
1123
1124 return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond)
1125 }
1126 }
1127
1128 // TestCheckModuleResourceAttrPair - as per TestCheckResourceAttrPair but with
1129 // support for non-root modules
1130 func TestCheckModuleResourceAttrPair(mpFirst []string, nameFirst string, keyFirst string, mpSecond []string, nameSecond string, keySecond string) TestCheckFunc {
1131 mptFirst := addrs.Module(mpFirst).UnkeyedInstanceShim()
1132 mptSecond := addrs.Module(mpSecond).UnkeyedInstanceShim()
1133 return func(s *terraform.State) error {
1134 isFirst, err := modulePathPrimaryInstanceState(s, mptFirst, nameFirst)
1135 if err != nil {
1136 return err
1137 }
1138
1139 isSecond, err := modulePathPrimaryInstanceState(s, mptSecond, nameSecond)
1140 if err != nil {
1141 return err
1142 }
1143
1144 return testCheckResourceAttrPair(isFirst, nameFirst, keyFirst, isSecond, nameSecond, keySecond)
1145 }
1146 }
1147
1148 func testCheckResourceAttrPair(isFirst *terraform.InstanceState, nameFirst string, keyFirst string, isSecond *terraform.InstanceState, nameSecond string, keySecond string) error {
1149 vFirst, okFirst := isFirst.Attributes[keyFirst]
1150 vSecond, okSecond := isSecond.Attributes[keySecond]
1151
1152 // Container count values of 0 should not be relied upon, and not reliably
1153 // maintained by helper/schema. For the purpose of tests, consider unset and
1154 // 0 to be equal.
1155 if len(keyFirst) > 2 && len(keySecond) > 2 && keyFirst[len(keyFirst)-2:] == keySecond[len(keySecond)-2:] &&
1156 (strings.HasSuffix(keyFirst, ".#") || strings.HasSuffix(keyFirst, ".%")) {
1157 // they have the same suffix, and it is a collection count key.
1158 if vFirst == "0" || vFirst == "" {
1159 okFirst = false
1160 }
1161 if vSecond == "0" || vSecond == "" {
1162 okSecond = false
1163 }
1164 }
1165
1166 if okFirst != okSecond {
1167 if !okFirst {
1168 return fmt.Errorf("%s: Attribute %q not set, but %q is set in %s as %q", nameFirst, keyFirst, keySecond, nameSecond, vSecond)
1169 }
1170 return fmt.Errorf("%s: Attribute %q is %q, but %q is not set in %s", nameFirst, keyFirst, vFirst, keySecond, nameSecond)
1171 }
1172 if !(okFirst || okSecond) {
1173 // If they both don't exist then they are equally unset, so that's okay.
1174 return nil
1175 }
1176
1177 if vFirst != vSecond {
1178 return fmt.Errorf(
1179 "%s: Attribute '%s' expected %#v, got %#v",
1180 nameFirst,
1181 keyFirst,
1182 vSecond,
1183 vFirst)
1184 }
1185
1186 return nil
1187 }
1188
1189 // TestCheckOutput checks an output in the Terraform configuration
1190 func TestCheckOutput(name, value string) TestCheckFunc {
1191 return func(s *terraform.State) error {
1192 ms := s.RootModule()
1193 rs, ok := ms.Outputs[name]
1194 if !ok {
1195 return fmt.Errorf("Not found: %s", name)
1196 }
1197
1198 if rs.Value != value {
1199 return fmt.Errorf(
1200 "Output '%s': expected %#v, got %#v",
1201 name,
1202 value,
1203 rs)
1204 }
1205
1206 return nil
1207 }
1208 }
1209
1210 func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc {
1211 return func(s *terraform.State) error {
1212 ms := s.RootModule()
1213 rs, ok := ms.Outputs[name]
1214 if !ok {
1215 return fmt.Errorf("Not found: %s", name)
1216 }
1217
1218 if !r.MatchString(rs.Value.(string)) {
1219 return fmt.Errorf(
1220 "Output '%s': %#v didn't match %q",
1221 name,
1222 rs,
1223 r.String())
1224 }
1225
1226 return nil
1227 }
1228 }
1229
1230 // TestT is the interface used to handle the test lifecycle of a test.
1231 //
1232 // Users should just use a *testing.T object, which implements this.
1233 type TestT interface {
1234 Error(args ...interface{})
1235 Fatal(args ...interface{})
1236 Skip(args ...interface{})
1237 Name() string
1238 Parallel()
1239 }
1240
1241 // This is set to true by unit tests to alter some behavior
1242 var testTesting = false
1243
1244 // modulePrimaryInstanceState returns the instance state for the given resource
1245 // name in a ModuleState
1246 func modulePrimaryInstanceState(s *terraform.State, ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) {
1247 rs, ok := ms.Resources[name]
1248 if !ok {
1249 return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path)
1250 }
1251
1252 is := rs.Primary
1253 if is == nil {
1254 return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path)
1255 }
1256
1257 return is, nil
1258 }
1259
1260 // modulePathPrimaryInstanceState returns the primary instance state for the
1261 // given resource name in a given module path.
1262 func modulePathPrimaryInstanceState(s *terraform.State, mp addrs.ModuleInstance, name string) (*terraform.InstanceState, error) {
1263 ms := s.ModuleByPath(mp)
1264 if ms == nil {
1265 return nil, fmt.Errorf("No module found at: %s", mp)
1266 }
1267
1268 return modulePrimaryInstanceState(s, ms, name)
1269 }
1270
1271 // primaryInstanceState returns the primary instance state for the given
1272 // resource name in the root module.
1273 func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) {
1274 ms := s.RootModule()
1275 return modulePrimaryInstanceState(s, ms, name)
1276 }
1277
1278 // operationError is a specialized implementation of error used to describe
1279 // failures during one of the several operations performed for a particular
1280 // test case.
1281 type operationError struct {
1282 OpName string
1283 Diags tfdiags.Diagnostics
1284 }
1285
1286 func newOperationError(opName string, diags tfdiags.Diagnostics) error {
1287 return operationError{opName, diags}
1288 }
1289
1290 // Error returns a terse error string containing just the basic diagnostic
1291 // messages, for situations where normal Go error behavior is appropriate.
1292 func (err operationError) Error() string {
1293 return fmt.Sprintf("errors during %s: %s", err.OpName, err.Diags.Err().Error())
1294 }
1295
1296 // ErrorDetail is like Error except it includes verbosely-rendered diagnostics
1297 // similar to what would come from a normal Terraform run, which include
1298 // additional context not included in Error().
1299 func (err operationError) ErrorDetail() string {
1300 var buf bytes.Buffer
1301 fmt.Fprintf(&buf, "errors during %s:", err.OpName)
1302 clr := &colorstring.Colorize{Disable: true, Colors: colorstring.DefaultColors}
1303 for _, diag := range err.Diags {
1304 diagStr := format.Diagnostic(diag, nil, clr, 78)
1305 buf.WriteByte('\n')
1306 buf.WriteString(diagStr)
1307 }
1308 return buf.String()
1309 }
1310
1311 // detailedErrorMessage is a helper for calling ErrorDetail on an error if
1312 // it is an operationError or just taking Error otherwise.
1313 func detailedErrorMessage(err error) string {
1314 switch tErr := err.(type) {
1315 case operationError:
1316 return tErr.ErrorDetail()
1317 default:
1318 return err.Error()
1319 }
1320 }