github.com/nevins-b/terraform@v0.3.8-0.20170215184714-bbae22007d5a/helper/resource/testing.go (about)

     1  package resource
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"regexp"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/davecgh/go-spew/spew"
    16  	"github.com/hashicorp/go-getter"
    17  	"github.com/hashicorp/go-multierror"
    18  	"github.com/hashicorp/terraform/config/module"
    19  	"github.com/hashicorp/terraform/helper/logging"
    20  	"github.com/hashicorp/terraform/terraform"
    21  )
    22  
    23  const TestEnvVar = "TF_ACC"
    24  
    25  // TestCheckFunc is the callback type used with acceptance tests to check
    26  // the state of a resource. The state passed in is the latest state known,
    27  // or in the case of being after a destroy, it is the last known state when
    28  // it was created.
    29  type TestCheckFunc func(*terraform.State) error
    30  
    31  // ImportStateCheckFunc is the check function for ImportState tests
    32  type ImportStateCheckFunc func([]*terraform.InstanceState) error
    33  
    34  // TestCase is a single acceptance test case used to test the apply/destroy
    35  // lifecycle of a resource in a specific configuration.
    36  //
    37  // When the destroy plan is executed, the config from the last TestStep
    38  // is used to plan it.
    39  type TestCase struct {
    40  	// IsUnitTest allows a test to run regardless of the TF_ACC
    41  	// environment variable. This should be used with care - only for
    42  	// fast tests on local resources (e.g. remote state with a local
    43  	// backend) but can be used to increase confidence in correct
    44  	// operation of Terraform without waiting for a full acctest run.
    45  	IsUnitTest bool
    46  
    47  	// PreCheck, if non-nil, will be called before any test steps are
    48  	// executed. It will only be executed in the case that the steps
    49  	// would run, so it can be used for some validation before running
    50  	// acceptance tests, such as verifying that keys are setup.
    51  	PreCheck func()
    52  
    53  	// Providers is the ResourceProvider that will be under test.
    54  	//
    55  	// Alternately, ProviderFactories can be specified for the providers
    56  	// that are valid. This takes priority over Providers.
    57  	//
    58  	// The end effect of each is the same: specifying the providers that
    59  	// are used within the tests.
    60  	Providers         map[string]terraform.ResourceProvider
    61  	ProviderFactories map[string]terraform.ResourceProviderFactory
    62  
    63  	// PreventPostDestroyRefresh can be set to true for cases where data sources
    64  	// are tested alongside real resources
    65  	PreventPostDestroyRefresh bool
    66  
    67  	// CheckDestroy is called after the resource is finally destroyed
    68  	// to allow the tester to test that the resource is truly gone.
    69  	CheckDestroy TestCheckFunc
    70  
    71  	// Steps are the apply sequences done within the context of the
    72  	// same state. Each step can have its own check to verify correctness.
    73  	Steps []TestStep
    74  
    75  	// The settings below control the "ID-only refresh test." This is
    76  	// an enabled-by-default test that tests that a refresh can be
    77  	// refreshed with only an ID to result in the same attributes.
    78  	// This validates completeness of Refresh.
    79  	//
    80  	// IDRefreshName is the name of the resource to check. This will
    81  	// default to the first non-nil primary resource in the state.
    82  	//
    83  	// IDRefreshIgnore is a list of configuration keys that will be ignored.
    84  	IDRefreshName   string
    85  	IDRefreshIgnore []string
    86  }
    87  
    88  // TestStep is a single apply sequence of a test, done within the
    89  // context of a state.
    90  //
    91  // Multiple TestSteps can be sequenced in a Test to allow testing
    92  // potentially complex update logic. In general, simply create/destroy
    93  // tests will only need one step.
    94  type TestStep struct {
    95  	// ResourceName should be set to the name of the resource
    96  	// that is being tested. Example: "aws_instance.foo". Various test
    97  	// modes use this to auto-detect state information.
    98  	//
    99  	// This is only required if the test mode settings below say it is
   100  	// for the mode you're using.
   101  	ResourceName string
   102  
   103  	// PreConfig is called before the Config is applied to perform any per-step
   104  	// setup that needs to happen. This is called regardless of "test mode"
   105  	// below.
   106  	PreConfig func()
   107  
   108  	//---------------------------------------------------------------
   109  	// Test modes. One of the following groups of settings must be
   110  	// set to determine what the test step will do. Ideally we would've
   111  	// used Go interfaces here but there are now hundreds of tests we don't
   112  	// want to re-type so instead we just determine which step logic
   113  	// to run based on what settings below are set.
   114  	//---------------------------------------------------------------
   115  
   116  	//---------------------------------------------------------------
   117  	// Plan, Apply testing
   118  	//---------------------------------------------------------------
   119  
   120  	// Config a string of the configuration to give to Terraform. If this
   121  	// is set, then the TestCase will execute this step with the same logic
   122  	// as a `terraform apply`.
   123  	Config string
   124  
   125  	// Check is called after the Config is applied. Use this step to
   126  	// make your own API calls to check the status of things, and to
   127  	// inspect the format of the ResourceState itself.
   128  	//
   129  	// If an error is returned, the test will fail. In this case, a
   130  	// destroy plan will still be attempted.
   131  	//
   132  	// If this is nil, no check is done on this step.
   133  	Check TestCheckFunc
   134  
   135  	// Destroy will create a destroy plan if set to true.
   136  	Destroy bool
   137  
   138  	// ExpectNonEmptyPlan can be set to true for specific types of tests that are
   139  	// looking to verify that a diff occurs
   140  	ExpectNonEmptyPlan bool
   141  
   142  	// ExpectError allows the construction of test cases that we expect to fail
   143  	// with an error. The specified regexp must match against the error for the
   144  	// test to pass.
   145  	ExpectError *regexp.Regexp
   146  
   147  	// PreventPostDestroyRefresh can be set to true for cases where data sources
   148  	// are tested alongside real resources
   149  	PreventPostDestroyRefresh bool
   150  
   151  	//---------------------------------------------------------------
   152  	// ImportState testing
   153  	//---------------------------------------------------------------
   154  
   155  	// ImportState, if true, will test the functionality of ImportState
   156  	// by importing the resource with ResourceName (must be set) and the
   157  	// ID of that resource.
   158  	ImportState bool
   159  
   160  	// ImportStateId is the ID to perform an ImportState operation with.
   161  	// This is optional. If it isn't set, then the resource ID is automatically
   162  	// determined by inspecting the state for ResourceName's ID.
   163  	ImportStateId string
   164  
   165  	// ImportStateCheck checks the results of ImportState. It should be
   166  	// used to verify that the resulting value of ImportState has the
   167  	// proper resources, IDs, and attributes.
   168  	ImportStateCheck ImportStateCheckFunc
   169  
   170  	// ImportStateVerify, if true, will also check that the state values
   171  	// that are finally put into the state after import match for all the
   172  	// IDs returned by the Import.
   173  	//
   174  	// ImportStateVerifyIgnore are fields that should not be verified to
   175  	// be equal. These can be set to ephemeral fields or fields that can't
   176  	// be refreshed and don't matter.
   177  	ImportStateVerify       bool
   178  	ImportStateVerifyIgnore []string
   179  }
   180  
   181  // Test performs an acceptance test on a resource.
   182  //
   183  // Tests are not run unless an environmental variable "TF_ACC" is
   184  // set to some non-empty value. This is to avoid test cases surprising
   185  // a user by creating real resources.
   186  //
   187  // Tests will fail unless the verbose flag (`go test -v`, or explicitly
   188  // the "-test.v" flag) is set. Because some acceptance tests take quite
   189  // long, we require the verbose flag so users are able to see progress
   190  // output.
   191  func Test(t TestT, c TestCase) {
   192  	// We only run acceptance tests if an env var is set because they're
   193  	// slow and generally require some outside configuration. You can opt out
   194  	// of this with OverrideEnvVar on individual TestCases.
   195  	if os.Getenv(TestEnvVar) == "" && !c.IsUnitTest {
   196  		t.Skip(fmt.Sprintf(
   197  			"Acceptance tests skipped unless env '%s' set",
   198  			TestEnvVar))
   199  		return
   200  	}
   201  
   202  	logWriter, err := logging.LogOutput()
   203  	if err != nil {
   204  		t.Error(fmt.Errorf("error setting up logging: %s", err))
   205  	}
   206  	log.SetOutput(logWriter)
   207  
   208  	// We require verbose mode so that the user knows what is going on.
   209  	if !testTesting && !testing.Verbose() && !c.IsUnitTest {
   210  		t.Fatal("Acceptance tests must be run with the -v flag on tests")
   211  		return
   212  	}
   213  
   214  	// Run the PreCheck if we have it
   215  	if c.PreCheck != nil {
   216  		c.PreCheck()
   217  	}
   218  
   219  	// Build our context options that we can
   220  	ctxProviders := c.ProviderFactories
   221  	if ctxProviders == nil {
   222  		ctxProviders = make(map[string]terraform.ResourceProviderFactory)
   223  		for k, p := range c.Providers {
   224  			ctxProviders[k] = terraform.ResourceProviderFactoryFixed(p)
   225  		}
   226  	}
   227  	opts := terraform.ContextOpts{Providers: ctxProviders}
   228  
   229  	// A single state variable to track the lifecycle, starting with no state
   230  	var state *terraform.State
   231  
   232  	// Go through each step and run it
   233  	var idRefreshCheck *terraform.ResourceState
   234  	idRefresh := c.IDRefreshName != ""
   235  	errored := false
   236  	for i, step := range c.Steps {
   237  		var err error
   238  		log.Printf("[WARN] Test: Executing step %d", i)
   239  
   240  		// Determine the test mode to execute
   241  		if step.Config != "" {
   242  			state, err = testStepConfig(opts, state, step)
   243  		} else if step.ImportState {
   244  			state, err = testStepImportState(opts, state, step)
   245  		} else {
   246  			err = fmt.Errorf(
   247  				"unknown test mode for step. Please see TestStep docs\n\n%#v",
   248  				step)
   249  		}
   250  
   251  		// If there was an error, exit
   252  		if err != nil {
   253  			// Perhaps we expected an error? Check if it matches
   254  			if step.ExpectError != nil {
   255  				if !step.ExpectError.MatchString(err.Error()) {
   256  					errored = true
   257  					t.Error(fmt.Sprintf(
   258  						"Step %d, expected error:\n\n%s\n\nTo match:\n\n%s\n\n",
   259  						i, err, step.ExpectError))
   260  					break
   261  				}
   262  			} else {
   263  				errored = true
   264  				t.Error(fmt.Sprintf(
   265  					"Step %d error: %s", i, err))
   266  				break
   267  			}
   268  		}
   269  
   270  		// If we've never checked an id-only refresh and our state isn't
   271  		// empty, find the first resource and test it.
   272  		if idRefresh && idRefreshCheck == nil && !state.Empty() {
   273  			// Find the first non-nil resource in the state
   274  			for _, m := range state.Modules {
   275  				if len(m.Resources) > 0 {
   276  					if v, ok := m.Resources[c.IDRefreshName]; ok {
   277  						idRefreshCheck = v
   278  					}
   279  
   280  					break
   281  				}
   282  			}
   283  
   284  			// If we have an instance to check for refreshes, do it
   285  			// immediately. We do it in the middle of another test
   286  			// because it shouldn't affect the overall state (refresh
   287  			// is read-only semantically) and we want to fail early if
   288  			// this fails. If refresh isn't read-only, then this will have
   289  			// caught a different bug.
   290  			if idRefreshCheck != nil {
   291  				log.Printf(
   292  					"[WARN] Test: Running ID-only refresh check on %s",
   293  					idRefreshCheck.Primary.ID)
   294  				if err := testIDOnlyRefresh(c, opts, step, idRefreshCheck); err != nil {
   295  					log.Printf("[ERROR] Test: ID-only test failed: %s", err)
   296  					t.Error(fmt.Sprintf(
   297  						"[ERROR] Test: ID-only test failed: %s", err))
   298  					break
   299  				}
   300  			}
   301  		}
   302  	}
   303  
   304  	// If we never checked an id-only refresh, it is a failure.
   305  	if idRefresh {
   306  		if !errored && len(c.Steps) > 0 && idRefreshCheck == nil {
   307  			t.Error("ID-only refresh check never ran.")
   308  		}
   309  	}
   310  
   311  	// If we have a state, then run the destroy
   312  	if state != nil {
   313  		lastStep := c.Steps[len(c.Steps)-1]
   314  		destroyStep := TestStep{
   315  			Config:                    lastStep.Config,
   316  			Check:                     c.CheckDestroy,
   317  			Destroy:                   true,
   318  			PreventPostDestroyRefresh: c.PreventPostDestroyRefresh,
   319  		}
   320  
   321  		log.Printf("[WARN] Test: Executing destroy step")
   322  		state, err := testStep(opts, state, destroyStep)
   323  		if err != nil {
   324  			t.Error(fmt.Sprintf(
   325  				"Error destroying resource! WARNING: Dangling resources\n"+
   326  					"may exist. The full state and error is shown below.\n\n"+
   327  					"Error: %s\n\nState: %s",
   328  				err,
   329  				state))
   330  		}
   331  	} else {
   332  		log.Printf("[WARN] Skipping destroy test since there is no state.")
   333  	}
   334  }
   335  
   336  // UnitTest is a helper to force the acceptance testing harness to run in the
   337  // normal unit test suite. This should only be used for resource that don't
   338  // have any external dependencies.
   339  func UnitTest(t TestT, c TestCase) {
   340  	c.IsUnitTest = true
   341  	Test(t, c)
   342  }
   343  
   344  func testIDOnlyRefresh(c TestCase, opts terraform.ContextOpts, step TestStep, r *terraform.ResourceState) error {
   345  	// TODO: We guard by this right now so master doesn't explode. We
   346  	// need to remove this eventually to make this part of the normal tests.
   347  	if os.Getenv("TF_ACC_IDONLY") == "" {
   348  		return nil
   349  	}
   350  
   351  	name := fmt.Sprintf("%s.foo", r.Type)
   352  
   353  	// Build the state. The state is just the resource with an ID. There
   354  	// are no attributes. We only set what is needed to perform a refresh.
   355  	state := terraform.NewState()
   356  	state.RootModule().Resources[name] = &terraform.ResourceState{
   357  		Type: r.Type,
   358  		Primary: &terraform.InstanceState{
   359  			ID: r.Primary.ID,
   360  		},
   361  	}
   362  
   363  	// Create the config module. We use the full config because Refresh
   364  	// doesn't have access to it and we may need things like provider
   365  	// configurations. The initial implementation of id-only checks used
   366  	// an empty config module, but that caused the aforementioned problems.
   367  	mod, err := testModule(opts, step)
   368  	if err != nil {
   369  		return err
   370  	}
   371  
   372  	// Initialize the context
   373  	opts.Module = mod
   374  	opts.State = state
   375  	ctx, err := terraform.NewContext(&opts)
   376  	if err != nil {
   377  		return err
   378  	}
   379  	if ws, es := ctx.Validate(); len(ws) > 0 || len(es) > 0 {
   380  		if len(es) > 0 {
   381  			estrs := make([]string, len(es))
   382  			for i, e := range es {
   383  				estrs[i] = e.Error()
   384  			}
   385  			return fmt.Errorf(
   386  				"Configuration is invalid.\n\nWarnings: %#v\n\nErrors: %#v",
   387  				ws, estrs)
   388  		}
   389  
   390  		log.Printf("[WARN] Config warnings: %#v", ws)
   391  	}
   392  
   393  	// Refresh!
   394  	state, err = ctx.Refresh()
   395  	if err != nil {
   396  		return fmt.Errorf("Error refreshing: %s", err)
   397  	}
   398  
   399  	// Verify attribute equivalence.
   400  	actualR := state.RootModule().Resources[name]
   401  	if actualR == nil {
   402  		return fmt.Errorf("Resource gone!")
   403  	}
   404  	if actualR.Primary == nil {
   405  		return fmt.Errorf("Resource has no primary instance")
   406  	}
   407  	actual := actualR.Primary.Attributes
   408  	expected := r.Primary.Attributes
   409  	// Remove fields we're ignoring
   410  	for _, v := range c.IDRefreshIgnore {
   411  		for k, _ := range actual {
   412  			if strings.HasPrefix(k, v) {
   413  				delete(actual, k)
   414  			}
   415  		}
   416  		for k, _ := range expected {
   417  			if strings.HasPrefix(k, v) {
   418  				delete(expected, k)
   419  			}
   420  		}
   421  	}
   422  
   423  	if !reflect.DeepEqual(actual, expected) {
   424  		// Determine only the different attributes
   425  		for k, v := range expected {
   426  			if av, ok := actual[k]; ok && v == av {
   427  				delete(expected, k)
   428  				delete(actual, k)
   429  			}
   430  		}
   431  
   432  		spewConf := spew.NewDefaultConfig()
   433  		spewConf.SortKeys = true
   434  		return fmt.Errorf(
   435  			"Attributes not equivalent. Difference is shown below. Top is actual, bottom is expected."+
   436  				"\n\n%s\n\n%s",
   437  			spewConf.Sdump(actual), spewConf.Sdump(expected))
   438  	}
   439  
   440  	return nil
   441  }
   442  
   443  func testModule(
   444  	opts terraform.ContextOpts,
   445  	step TestStep) (*module.Tree, error) {
   446  	if step.PreConfig != nil {
   447  		step.PreConfig()
   448  	}
   449  
   450  	cfgPath, err := ioutil.TempDir("", "tf-test")
   451  	if err != nil {
   452  		return nil, fmt.Errorf(
   453  			"Error creating temporary directory for config: %s", err)
   454  	}
   455  	defer os.RemoveAll(cfgPath)
   456  
   457  	// Write the configuration
   458  	cfgF, err := os.Create(filepath.Join(cfgPath, "main.tf"))
   459  	if err != nil {
   460  		return nil, fmt.Errorf(
   461  			"Error creating temporary file for config: %s", err)
   462  	}
   463  
   464  	_, err = io.Copy(cfgF, strings.NewReader(step.Config))
   465  	cfgF.Close()
   466  	if err != nil {
   467  		return nil, fmt.Errorf(
   468  			"Error creating temporary file for config: %s", err)
   469  	}
   470  
   471  	// Parse the configuration
   472  	mod, err := module.NewTreeModule("", cfgPath)
   473  	if err != nil {
   474  		return nil, fmt.Errorf(
   475  			"Error loading configuration: %s", err)
   476  	}
   477  
   478  	// Load the modules
   479  	modStorage := &getter.FolderStorage{
   480  		StorageDir: filepath.Join(cfgPath, ".tfmodules"),
   481  	}
   482  	err = mod.Load(modStorage, module.GetModeGet)
   483  	if err != nil {
   484  		return nil, fmt.Errorf("Error downloading modules: %s", err)
   485  	}
   486  
   487  	return mod, nil
   488  }
   489  
   490  func testResource(c TestStep, state *terraform.State) (*terraform.ResourceState, error) {
   491  	if c.ResourceName == "" {
   492  		return nil, fmt.Errorf("ResourceName must be set in TestStep")
   493  	}
   494  
   495  	for _, m := range state.Modules {
   496  		if len(m.Resources) > 0 {
   497  			if v, ok := m.Resources[c.ResourceName]; ok {
   498  				return v, nil
   499  			}
   500  		}
   501  	}
   502  
   503  	return nil, fmt.Errorf(
   504  		"Resource specified by ResourceName couldn't be found: %s", c.ResourceName)
   505  }
   506  
   507  // ComposeTestCheckFunc lets you compose multiple TestCheckFuncs into
   508  // a single TestCheckFunc.
   509  //
   510  // As a user testing their provider, this lets you decompose your checks
   511  // into smaller pieces more easily.
   512  func ComposeTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc {
   513  	return func(s *terraform.State) error {
   514  		for i, f := range fs {
   515  			if err := f(s); err != nil {
   516  				return fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err)
   517  			}
   518  		}
   519  
   520  		return nil
   521  	}
   522  }
   523  
   524  // ComposeAggregateTestCheckFunc lets you compose multiple TestCheckFuncs into
   525  // a single TestCheckFunc.
   526  //
   527  // As a user testing their provider, this lets you decompose your checks
   528  // into smaller pieces more easily.
   529  //
   530  // Unlike ComposeTestCheckFunc, ComposeAggergateTestCheckFunc runs _all_ of the
   531  // TestCheckFuncs and aggregates failures.
   532  func ComposeAggregateTestCheckFunc(fs ...TestCheckFunc) TestCheckFunc {
   533  	return func(s *terraform.State) error {
   534  		var result *multierror.Error
   535  
   536  		for i, f := range fs {
   537  			if err := f(s); err != nil {
   538  				result = multierror.Append(result, fmt.Errorf("Check %d/%d error: %s", i+1, len(fs), err))
   539  			}
   540  		}
   541  
   542  		return result.ErrorOrNil()
   543  	}
   544  }
   545  
   546  // TestCheckResourceAttrSet is a TestCheckFunc which ensures a value
   547  // exists in state for the given name/key combination. It is useful when
   548  // testing that computed values were set, when it is not possible to
   549  // know ahead of time what the values will be.
   550  func TestCheckResourceAttrSet(name, key string) TestCheckFunc {
   551  	return func(s *terraform.State) error {
   552  		is, err := primaryInstanceState(s, name)
   553  		if err != nil {
   554  			return err
   555  		}
   556  
   557  		if val, ok := is.Attributes[key]; ok && val != "" {
   558  			return nil
   559  		}
   560  
   561  		return fmt.Errorf("%s: Attribute '%s' expected to be set", name, key)
   562  	}
   563  }
   564  
   565  // TestCheckResourceAttr is a TestCheckFunc which validates
   566  // the value in state for the given name/key combination.
   567  func TestCheckResourceAttr(name, key, value string) TestCheckFunc {
   568  	return func(s *terraform.State) error {
   569  		is, err := primaryInstanceState(s, name)
   570  		if err != nil {
   571  			return err
   572  		}
   573  
   574  		if v, ok := is.Attributes[key]; !ok || v != value {
   575  			if !ok {
   576  				return fmt.Errorf("%s: Attribute '%s' not found", name, key)
   577  			}
   578  
   579  			return fmt.Errorf(
   580  				"%s: Attribute '%s' expected %#v, got %#v",
   581  				name,
   582  				key,
   583  				value,
   584  				v)
   585  		}
   586  
   587  		return nil
   588  	}
   589  }
   590  
   591  // TestCheckNoResourceAttr is a TestCheckFunc which ensures that
   592  // NO value exists in state for the given name/key combination.
   593  func TestCheckNoResourceAttr(name, key string) TestCheckFunc {
   594  	return func(s *terraform.State) error {
   595  		is, err := primaryInstanceState(s, name)
   596  		if err != nil {
   597  			return err
   598  		}
   599  
   600  		if _, ok := is.Attributes[key]; ok {
   601  			return fmt.Errorf("%s: Attribute '%s' found when not expected", name, key)
   602  		}
   603  
   604  		return nil
   605  	}
   606  }
   607  
   608  // TestMatchResourceAttr is a TestCheckFunc which checks that the value
   609  // in state for the given name/key combination matches the given regex.
   610  func TestMatchResourceAttr(name, key string, r *regexp.Regexp) TestCheckFunc {
   611  	return func(s *terraform.State) error {
   612  		is, err := primaryInstanceState(s, name)
   613  		if err != nil {
   614  			return err
   615  		}
   616  
   617  		if !r.MatchString(is.Attributes[key]) {
   618  			return fmt.Errorf(
   619  				"%s: Attribute '%s' didn't match %q, got %#v",
   620  				name,
   621  				key,
   622  				r.String(),
   623  				is.Attributes[key])
   624  		}
   625  
   626  		return nil
   627  	}
   628  }
   629  
   630  // TestCheckResourceAttrPtr is like TestCheckResourceAttr except the
   631  // value is a pointer so that it can be updated while the test is running.
   632  // It will only be dereferenced at the point this step is run.
   633  func TestCheckResourceAttrPtr(name string, key string, value *string) TestCheckFunc {
   634  	return func(s *terraform.State) error {
   635  		return TestCheckResourceAttr(name, key, *value)(s)
   636  	}
   637  }
   638  
   639  // TestCheckResourceAttrPair is a TestCheckFunc which validates that the values
   640  // in state for a pair of name/key combinations are equal.
   641  func TestCheckResourceAttrPair(nameFirst, keyFirst, nameSecond, keySecond string) TestCheckFunc {
   642  	return func(s *terraform.State) error {
   643  		isFirst, err := primaryInstanceState(s, nameFirst)
   644  		if err != nil {
   645  			return err
   646  		}
   647  		vFirst, ok := isFirst.Attributes[keyFirst]
   648  		if !ok {
   649  			return fmt.Errorf("%s: Attribute '%s' not found", nameFirst, keyFirst)
   650  		}
   651  
   652  		isSecond, err := primaryInstanceState(s, nameSecond)
   653  		if err != nil {
   654  			return err
   655  		}
   656  		vSecond, ok := isSecond.Attributes[keySecond]
   657  		if !ok {
   658  			return fmt.Errorf("%s: Attribute '%s' not found", nameSecond, keySecond)
   659  		}
   660  
   661  		if vFirst != vSecond {
   662  			return fmt.Errorf(
   663  				"%s: Attribute '%s' expected %#v, got %#v",
   664  				nameFirst,
   665  				keyFirst,
   666  				vSecond,
   667  				vFirst)
   668  		}
   669  
   670  		return nil
   671  	}
   672  }
   673  
   674  // TestCheckOutput checks an output in the Terraform configuration
   675  func TestCheckOutput(name, value string) TestCheckFunc {
   676  	return func(s *terraform.State) error {
   677  		ms := s.RootModule()
   678  		rs, ok := ms.Outputs[name]
   679  		if !ok {
   680  			return fmt.Errorf("Not found: %s", name)
   681  		}
   682  
   683  		if rs.Value != value {
   684  			return fmt.Errorf(
   685  				"Output '%s': expected %#v, got %#v",
   686  				name,
   687  				value,
   688  				rs)
   689  		}
   690  
   691  		return nil
   692  	}
   693  }
   694  
   695  func TestMatchOutput(name string, r *regexp.Regexp) TestCheckFunc {
   696  	return func(s *terraform.State) error {
   697  		ms := s.RootModule()
   698  		rs, ok := ms.Outputs[name]
   699  		if !ok {
   700  			return fmt.Errorf("Not found: %s", name)
   701  		}
   702  
   703  		if !r.MatchString(rs.Value.(string)) {
   704  			return fmt.Errorf(
   705  				"Output '%s': %#v didn't match %q",
   706  				name,
   707  				rs,
   708  				r.String())
   709  		}
   710  
   711  		return nil
   712  	}
   713  }
   714  
   715  // TestT is the interface used to handle the test lifecycle of a test.
   716  //
   717  // Users should just use a *testing.T object, which implements this.
   718  type TestT interface {
   719  	Error(args ...interface{})
   720  	Fatal(args ...interface{})
   721  	Skip(args ...interface{})
   722  }
   723  
   724  // This is set to true by unit tests to alter some behavior
   725  var testTesting = false
   726  
   727  // primaryInstanceState returns the primary instance state for the given resource name.
   728  func primaryInstanceState(s *terraform.State, name string) (*terraform.InstanceState, error) {
   729  	ms := s.RootModule()
   730  	rs, ok := ms.Resources[name]
   731  	if !ok {
   732  		return nil, fmt.Errorf("Not found: %s", name)
   733  	}
   734  
   735  	is := rs.Primary
   736  	if is == nil {
   737  		return nil, fmt.Errorf("No primary instance: %s", name)
   738  	}
   739  
   740  	return is, nil
   741  }