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