github.com/sylr/terraform@v0.11.12-beta1/helper/resource/testing.go (about)

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