github.com/hashicorp/terraform-plugin-sdk@v1.17.2/helper/resource/testing.go (about)

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