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