github.com/rstandt/terraform@v0.12.32-0.20230710220336-b1063613405c/command/command_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/md5"
     6  	"encoding/base64"
     7  	"encoding/json"
     8  	"flag"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"log"
    13  	"net/http"
    14  	"net/http/httptest"
    15  	"os"
    16  	"os/exec"
    17  	"path/filepath"
    18  	"strings"
    19  	"syscall"
    20  	"testing"
    21  
    22  	"github.com/hashicorp/terraform/internal/initwd"
    23  	"github.com/hashicorp/terraform/registry"
    24  
    25  	"github.com/hashicorp/terraform/addrs"
    26  	"github.com/hashicorp/terraform/configs"
    27  	"github.com/hashicorp/terraform/configs/configload"
    28  	"github.com/hashicorp/terraform/configs/configschema"
    29  	"github.com/hashicorp/terraform/helper/logging"
    30  	"github.com/hashicorp/terraform/plans"
    31  	"github.com/hashicorp/terraform/plans/planfile"
    32  	"github.com/hashicorp/terraform/providers"
    33  	"github.com/hashicorp/terraform/provisioners"
    34  	"github.com/hashicorp/terraform/states"
    35  	"github.com/hashicorp/terraform/states/statefile"
    36  	"github.com/hashicorp/terraform/states/statemgr"
    37  	"github.com/hashicorp/terraform/terraform"
    38  	"github.com/hashicorp/terraform/version"
    39  	"github.com/zclconf/go-cty/cty"
    40  
    41  	backendInit "github.com/hashicorp/terraform/backend/init"
    42  )
    43  
    44  // These are the directories for our test data and fixtures.
    45  var (
    46  	fixtureDir  = "./testdata"
    47  	testDataDir = "./testdata"
    48  )
    49  
    50  // a top level temp directory which will be cleaned after all tests
    51  var testingDir string
    52  
    53  func init() {
    54  	test = true
    55  
    56  	// Initialize the backends
    57  	backendInit.Init(nil)
    58  
    59  	// Expand the data and fixture dirs on init because
    60  	// we change the working directory in some tests.
    61  	var err error
    62  	fixtureDir, err = filepath.Abs(fixtureDir)
    63  	if err != nil {
    64  		panic(err)
    65  	}
    66  
    67  	testDataDir, err = filepath.Abs(testDataDir)
    68  	if err != nil {
    69  		panic(err)
    70  	}
    71  
    72  	testingDir, err = ioutil.TempDir(testingDir, "tf")
    73  	if err != nil {
    74  		panic(err)
    75  	}
    76  }
    77  
    78  func TestMain(m *testing.M) {
    79  	defer os.RemoveAll(testingDir)
    80  
    81  	flag.Parse()
    82  	if testing.Verbose() {
    83  		// if we're verbose, use the logging requested by TF_LOG
    84  		logging.SetOutput()
    85  	} else {
    86  		// otherwise silence all logs
    87  		log.SetOutput(ioutil.Discard)
    88  	}
    89  
    90  	// Make sure backend init is initialized, since our tests tend to assume it.
    91  	backendInit.Init(nil)
    92  
    93  	os.Exit(m.Run())
    94  }
    95  
    96  func tempDir(t *testing.T) string {
    97  	t.Helper()
    98  
    99  	dir, err := ioutil.TempDir(testingDir, "tf")
   100  	if err != nil {
   101  		t.Fatalf("err: %s", err)
   102  	}
   103  
   104  	dir, err = filepath.EvalSymlinks(dir)
   105  	if err != nil {
   106  		t.Fatal(err)
   107  	}
   108  
   109  	if err := os.RemoveAll(dir); err != nil {
   110  		t.Fatalf("err: %s", err)
   111  	}
   112  
   113  	return dir
   114  }
   115  
   116  func testFixturePath(name string) string {
   117  	return filepath.Join(fixtureDir, name)
   118  }
   119  
   120  func metaOverridesForProvider(p providers.Interface) *testingOverrides {
   121  	return &testingOverrides{
   122  		ProviderResolver: providers.ResolverFixed(
   123  			map[addrs.Provider]providers.Factory{
   124  				addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
   125  			},
   126  		),
   127  	}
   128  }
   129  
   130  func metaOverridesForProviderAndProvisioner(p providers.Interface, pr provisioners.Interface) *testingOverrides {
   131  	return &testingOverrides{
   132  		ProviderResolver: providers.ResolverFixed(
   133  			map[addrs.Provider]providers.Factory{
   134  				addrs.NewLegacyProvider("test"): providers.FactoryFixed(p),
   135  			},
   136  		),
   137  		Provisioners: map[string]provisioners.Factory{
   138  			"shell": provisioners.FactoryFixed(pr),
   139  		},
   140  	}
   141  }
   142  
   143  func testModule(t *testing.T, name string) *configs.Config {
   144  	t.Helper()
   145  	c, _ := testModuleWithSnapshot(t, name)
   146  	return c
   147  }
   148  
   149  func testModuleWithSnapshot(t *testing.T, name string) (*configs.Config, *configload.Snapshot) {
   150  	t.Helper()
   151  
   152  	dir := filepath.Join(fixtureDir, name)
   153  	// FIXME: We're not dealing with the cleanup function here because
   154  	// this testModule function is used all over and so we don't want to
   155  	// change its interface at this late stage.
   156  	loader, _ := configload.NewLoaderForTests(t)
   157  
   158  	// Test modules usually do not refer to remote sources, and for local
   159  	// sources only this ultimately just records all of the module paths
   160  	// in a JSON file so that we can load them below.
   161  	inst := initwd.NewModuleInstaller(loader.ModulesDir(), registry.NewClient(nil, nil))
   162  	_, instDiags := inst.InstallModules(dir, true, initwd.ModuleInstallHooksImpl{})
   163  	if instDiags.HasErrors() {
   164  		t.Fatal(instDiags.Err())
   165  	}
   166  
   167  	config, snap, diags := loader.LoadConfigWithSnapshot(dir)
   168  	if diags.HasErrors() {
   169  		t.Fatal(diags.Error())
   170  	}
   171  
   172  	return config, snap
   173  }
   174  
   175  // testPlan returns a non-nil noop plan.
   176  func testPlan(t *testing.T) *plans.Plan {
   177  	t.Helper()
   178  
   179  	// This is what an empty configuration block would look like after being
   180  	// decoded with the schema of the "local" backend.
   181  	backendConfig := cty.ObjectVal(map[string]cty.Value{
   182  		"path":          cty.NullVal(cty.String),
   183  		"workspace_dir": cty.NullVal(cty.String),
   184  	})
   185  	backendConfigRaw, err := plans.NewDynamicValue(backendConfig, backendConfig.Type())
   186  	if err != nil {
   187  		t.Fatal(err)
   188  	}
   189  
   190  	return &plans.Plan{
   191  		Backend: plans.Backend{
   192  			// This is just a placeholder so that the plan file can be written
   193  			// out. Caller may wish to override it to something more "real"
   194  			// where the plan will actually be subsequently applied.
   195  			Type:   "local",
   196  			Config: backendConfigRaw,
   197  		},
   198  		Changes: plans.NewChanges(),
   199  	}
   200  }
   201  
   202  func testPlanFile(t *testing.T, configSnap *configload.Snapshot, state *states.State, plan *plans.Plan) string {
   203  	t.Helper()
   204  
   205  	stateFile := &statefile.File{
   206  		Lineage:          "",
   207  		State:            state,
   208  		TerraformVersion: version.SemVer,
   209  	}
   210  
   211  	path := testTempFile(t)
   212  	err := planfile.Create(path, configSnap, stateFile, plan)
   213  	if err != nil {
   214  		t.Fatalf("failed to create temporary plan file: %s", err)
   215  	}
   216  
   217  	return path
   218  }
   219  
   220  // testPlanFileNoop is a shortcut function that creates a plan file that
   221  // represents no changes and returns its path. This is useful when a test
   222  // just needs any plan file, and it doesn't matter what is inside it.
   223  func testPlanFileNoop(t *testing.T) string {
   224  	snap := &configload.Snapshot{
   225  		Modules: map[string]*configload.SnapshotModule{
   226  			"": {
   227  				Dir: ".",
   228  				Files: map[string][]byte{
   229  					"main.tf": nil,
   230  				},
   231  			},
   232  		},
   233  	}
   234  	state := states.NewState()
   235  	plan := testPlan(t)
   236  	return testPlanFile(t, snap, state, plan)
   237  }
   238  
   239  func testReadPlan(t *testing.T, path string) *plans.Plan {
   240  	t.Helper()
   241  
   242  	f, err := planfile.Open(path)
   243  	if err != nil {
   244  		t.Fatalf("error opening plan file %q: %s", path, err)
   245  	}
   246  	defer f.Close()
   247  
   248  	p, err := f.ReadPlan()
   249  	if err != nil {
   250  		t.Fatalf("error reading plan from plan file %q: %s", path, err)
   251  	}
   252  
   253  	return p
   254  }
   255  
   256  // testState returns a test State structure that we use for a lot of tests.
   257  func testState() *states.State {
   258  	return states.BuildState(func(s *states.SyncState) {
   259  		s.SetResourceInstanceCurrent(
   260  			addrs.Resource{
   261  				Mode: addrs.ManagedResourceMode,
   262  				Type: "test_instance",
   263  				Name: "foo",
   264  			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
   265  			&states.ResourceInstanceObjectSrc{
   266  				// The weird whitespace here is reflective of how this would
   267  				// get written out in a real state file, due to the indentation
   268  				// of all of the containing wrapping objects and arrays.
   269  				AttrsJSON:    []byte("{\n            \"id\": \"bar\"\n          }"),
   270  				Status:       states.ObjectReady,
   271  				Dependencies: []addrs.AbsResource{},
   272  				DependsOn:    []addrs.Referenceable{},
   273  			},
   274  			addrs.ProviderConfig{
   275  				Type: addrs.NewLegacyProvider("test"),
   276  			}.Absolute(addrs.RootModuleInstance),
   277  		)
   278  		// DeepCopy is used here to ensure our synthetic state matches exactly
   279  		// with a state that will have been copied during the command
   280  		// operation, and all fields have been copied correctly.
   281  	}).DeepCopy()
   282  }
   283  
   284  // writeStateForTesting is a helper that writes the given naked state to the
   285  // given writer, generating a stub *statefile.File wrapper which is then
   286  // immediately discarded.
   287  func writeStateForTesting(state *states.State, w io.Writer) error {
   288  	sf := &statefile.File{
   289  		Serial:  0,
   290  		Lineage: "fake-for-testing",
   291  		State:   state,
   292  	}
   293  	return statefile.Write(sf, w)
   294  }
   295  
   296  // testStateMgrCurrentLineage returns the current lineage for the given state
   297  // manager, or the empty string if it does not use lineage. This is primarily
   298  // for testing against the local backend, which always supports lineage.
   299  func testStateMgrCurrentLineage(mgr statemgr.Persistent) string {
   300  	if pm, ok := mgr.(statemgr.PersistentMeta); ok {
   301  		m := pm.StateSnapshotMeta()
   302  		return m.Lineage
   303  	}
   304  	return ""
   305  }
   306  
   307  // markStateForMatching is a helper that writes a specific marker value to
   308  // a state so that it can be recognized later with getStateMatchingMarker.
   309  //
   310  // Internally this just sets a root module output value called "testing_mark"
   311  // to the given string value. If the state is being checked in other ways,
   312  // the test code may need to compensate for the addition or overwriting of this
   313  // special output value name.
   314  //
   315  // The given mark string is returned verbatim, to allow the following pattern
   316  // in tests:
   317  //
   318  //     mark := markStateForMatching(state, "foo")
   319  //     // (do stuff to the state)
   320  //     assertStateHasMarker(state, mark)
   321  func markStateForMatching(state *states.State, mark string) string {
   322  	state.RootModule().SetOutputValue("testing_mark", cty.StringVal(mark), false)
   323  	return mark
   324  }
   325  
   326  // getStateMatchingMarker is used with markStateForMatching to retrieve the
   327  // mark string previously added to the given state. If no such mark is present,
   328  // the result is an empty string.
   329  func getStateMatchingMarker(state *states.State) string {
   330  	os := state.RootModule().OutputValues["testing_mark"]
   331  	if os == nil {
   332  		return ""
   333  	}
   334  	v := os.Value
   335  	if v.Type() == cty.String && v.IsKnown() && !v.IsNull() {
   336  		return v.AsString()
   337  	}
   338  	return ""
   339  }
   340  
   341  // stateHasMarker is a helper around getStateMatchingMarker that also includes
   342  // the equality test, for more convenient use in test assertion branches.
   343  func stateHasMarker(state *states.State, want string) bool {
   344  	return getStateMatchingMarker(state) == want
   345  }
   346  
   347  // assertStateHasMarker wraps stateHasMarker to automatically generate a
   348  // fatal test result (i.e. t.Fatal) if the marker doesn't match.
   349  func assertStateHasMarker(t *testing.T, state *states.State, want string) {
   350  	if !stateHasMarker(state, want) {
   351  		t.Fatalf("wrong state marker\ngot:  %q\nwant: %q", getStateMatchingMarker(state), want)
   352  	}
   353  }
   354  
   355  func testStateFile(t *testing.T, s *states.State) string {
   356  	t.Helper()
   357  
   358  	path := testTempFile(t)
   359  
   360  	f, err := os.Create(path)
   361  	if err != nil {
   362  		t.Fatalf("failed to create temporary state file %s: %s", path, err)
   363  	}
   364  	defer f.Close()
   365  
   366  	err = writeStateForTesting(s, f)
   367  	if err != nil {
   368  		t.Fatalf("failed to write state to temporary file %s: %s", path, err)
   369  	}
   370  
   371  	return path
   372  }
   373  
   374  // testStateFileDefault writes the state out to the default statefile
   375  // in the cwd. Use `testCwd` to change into a temp cwd.
   376  func testStateFileDefault(t *testing.T, s *terraform.State) string {
   377  	t.Helper()
   378  
   379  	f, err := os.Create(DefaultStateFilename)
   380  	if err != nil {
   381  		t.Fatalf("err: %s", err)
   382  	}
   383  	defer f.Close()
   384  
   385  	if err := terraform.WriteState(s, f); err != nil {
   386  		t.Fatalf("err: %s", err)
   387  	}
   388  
   389  	return DefaultStateFilename
   390  }
   391  
   392  // testStateFileRemote writes the state out to the remote statefile
   393  // in the cwd. Use `testCwd` to change into a temp cwd.
   394  func testStateFileRemote(t *testing.T, s *terraform.State) string {
   395  	t.Helper()
   396  
   397  	path := filepath.Join(DefaultDataDir, DefaultStateFilename)
   398  	if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
   399  		t.Fatalf("err: %s", err)
   400  	}
   401  
   402  	f, err := os.Create(path)
   403  	if err != nil {
   404  		t.Fatalf("err: %s", err)
   405  	}
   406  	defer f.Close()
   407  
   408  	if err := terraform.WriteState(s, f); err != nil {
   409  		t.Fatalf("err: %s", err)
   410  	}
   411  
   412  	return path
   413  }
   414  
   415  // testStateRead reads the state from a file
   416  func testStateRead(t *testing.T, path string) *states.State {
   417  	t.Helper()
   418  
   419  	f, err := os.Open(path)
   420  	if err != nil {
   421  		t.Fatalf("err: %s", err)
   422  	}
   423  	defer f.Close()
   424  
   425  	sf, err := statefile.Read(f)
   426  	if err != nil {
   427  		t.Fatalf("err: %s", err)
   428  	}
   429  
   430  	return sf.State
   431  }
   432  
   433  // testDataStateRead reads a "data state", which is a file format resembling
   434  // our state format v3 that is used only to track current backend settings.
   435  //
   436  // This old format still uses *terraform.State, but should be replaced with
   437  // a more specialized type in a later release.
   438  func testDataStateRead(t *testing.T, path string) *terraform.State {
   439  	t.Helper()
   440  
   441  	f, err := os.Open(path)
   442  	if err != nil {
   443  		t.Fatalf("err: %s", err)
   444  	}
   445  	defer f.Close()
   446  
   447  	s, err := terraform.ReadState(f)
   448  	if err != nil {
   449  		t.Fatalf("err: %s", err)
   450  	}
   451  
   452  	return s
   453  }
   454  
   455  // testStateOutput tests that the state at the given path contains
   456  // the expected state string.
   457  func testStateOutput(t *testing.T, path string, expected string) {
   458  	t.Helper()
   459  
   460  	newState := testStateRead(t, path)
   461  	actual := strings.TrimSpace(newState.String())
   462  	expected = strings.TrimSpace(expected)
   463  	if actual != expected {
   464  		t.Fatalf("expected:\n%s\nactual:\n%s", expected, actual)
   465  	}
   466  }
   467  
   468  func testProvider() *terraform.MockProvider {
   469  	p := new(terraform.MockProvider)
   470  	p.PlanResourceChangeResponse = providers.PlanResourceChangeResponse{
   471  		PlannedState: cty.EmptyObjectVal,
   472  	}
   473  	p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
   474  		return providers.ReadResourceResponse{
   475  			NewState: req.PriorState,
   476  		}
   477  	}
   478  	return p
   479  }
   480  
   481  func testTempFile(t *testing.T) string {
   482  	t.Helper()
   483  
   484  	return filepath.Join(testTempDir(t), "state.tfstate")
   485  }
   486  
   487  func testTempDir(t *testing.T) string {
   488  	t.Helper()
   489  
   490  	d, err := ioutil.TempDir(testingDir, "tf")
   491  	if err != nil {
   492  		t.Fatalf("err: %s", err)
   493  	}
   494  
   495  	d, err = filepath.EvalSymlinks(d)
   496  	if err != nil {
   497  		t.Fatal(err)
   498  	}
   499  
   500  	return d
   501  }
   502  
   503  // testRename renames the path to new and returns a function to defer to
   504  // revert the rename.
   505  func testRename(t *testing.T, base, path, new string) func() {
   506  	t.Helper()
   507  
   508  	if base != "" {
   509  		path = filepath.Join(base, path)
   510  		new = filepath.Join(base, new)
   511  	}
   512  
   513  	if err := os.Rename(path, new); err != nil {
   514  		t.Fatalf("err: %s", err)
   515  	}
   516  
   517  	return func() {
   518  		// Just re-rename and ignore the return value
   519  		testRename(t, "", new, path)
   520  	}
   521  }
   522  
   523  // testChdir changes the directory and returns a function to defer to
   524  // revert the old cwd.
   525  func testChdir(t *testing.T, new string) func() {
   526  	t.Helper()
   527  
   528  	old, err := os.Getwd()
   529  	if err != nil {
   530  		t.Fatalf("err: %s", err)
   531  	}
   532  
   533  	if err := os.Chdir(new); err != nil {
   534  		t.Fatalf("err: %v", err)
   535  	}
   536  
   537  	return func() {
   538  		// Re-run the function ignoring the defer result
   539  		testChdir(t, old)
   540  	}
   541  }
   542  
   543  // testCwd is used to change the current working directory
   544  // into a test directory that should be remoted after
   545  func testCwd(t *testing.T) (string, string) {
   546  	t.Helper()
   547  
   548  	tmp, err := ioutil.TempDir(testingDir, "tf")
   549  	if err != nil {
   550  		t.Fatalf("err: %v", err)
   551  	}
   552  
   553  	cwd, err := os.Getwd()
   554  	if err != nil {
   555  		t.Fatalf("err: %v", err)
   556  	}
   557  
   558  	if err := os.Chdir(tmp); err != nil {
   559  		t.Fatalf("err: %v", err)
   560  	}
   561  
   562  	return tmp, cwd
   563  }
   564  
   565  // testFixCwd is used to as a defer to testDir
   566  func testFixCwd(t *testing.T, tmp, cwd string) {
   567  	t.Helper()
   568  
   569  	if err := os.Chdir(cwd); err != nil {
   570  		t.Fatalf("err: %v", err)
   571  	}
   572  
   573  	if err := os.RemoveAll(tmp); err != nil {
   574  		t.Fatalf("err: %v", err)
   575  	}
   576  }
   577  
   578  // testStdinPipe changes os.Stdin to be a pipe that sends the data from
   579  // the reader before closing the pipe.
   580  //
   581  // The returned function should be deferred to properly clean up and restore
   582  // the original stdin.
   583  func testStdinPipe(t *testing.T, src io.Reader) func() {
   584  	t.Helper()
   585  
   586  	r, w, err := os.Pipe()
   587  	if err != nil {
   588  		t.Fatalf("err: %s", err)
   589  	}
   590  
   591  	// Modify stdin to point to our new pipe
   592  	old := os.Stdin
   593  	os.Stdin = r
   594  
   595  	// Copy the data from the reader to the pipe
   596  	go func() {
   597  		defer w.Close()
   598  		io.Copy(w, src)
   599  	}()
   600  
   601  	return func() {
   602  		// Close our read end
   603  		r.Close()
   604  
   605  		// Reset stdin
   606  		os.Stdin = old
   607  	}
   608  }
   609  
   610  // Modify os.Stdout to write to the given buffer. Note that this is generally
   611  // not useful since the commands are configured to write to a cli.Ui, not
   612  // Stdout directly. Commands like `console` though use the raw stdout.
   613  func testStdoutCapture(t *testing.T, dst io.Writer) func() {
   614  	t.Helper()
   615  
   616  	r, w, err := os.Pipe()
   617  	if err != nil {
   618  		t.Fatalf("err: %s", err)
   619  	}
   620  
   621  	// Modify stdout
   622  	old := os.Stdout
   623  	os.Stdout = w
   624  
   625  	// Copy
   626  	doneCh := make(chan struct{})
   627  	go func() {
   628  		defer close(doneCh)
   629  		defer r.Close()
   630  		io.Copy(dst, r)
   631  	}()
   632  
   633  	return func() {
   634  		// Close the writer end of the pipe
   635  		w.Sync()
   636  		w.Close()
   637  
   638  		// Reset stdout
   639  		os.Stdout = old
   640  
   641  		// Wait for the data copy to complete to avoid a race reading data
   642  		<-doneCh
   643  	}
   644  }
   645  
   646  // testInteractiveInput configures tests so that the answers given are sent
   647  // in order to interactive prompts. The returned function must be called
   648  // in a defer to clean up.
   649  func testInteractiveInput(t *testing.T, answers []string) func() {
   650  	t.Helper()
   651  
   652  	// Disable test mode so input is called
   653  	test = false
   654  
   655  	// Setup reader/writers
   656  	testInputResponse = answers
   657  	defaultInputReader = bytes.NewBufferString("")
   658  	defaultInputWriter = new(bytes.Buffer)
   659  
   660  	// Return the cleanup
   661  	return func() {
   662  		test = true
   663  		testInputResponse = nil
   664  	}
   665  }
   666  
   667  // testInputMap configures tests so that the given answers are returned
   668  // for calls to Input when the right question is asked. The key is the
   669  // question "Id" that is used.
   670  func testInputMap(t *testing.T, answers map[string]string) func() {
   671  	t.Helper()
   672  
   673  	// Disable test mode so input is called
   674  	test = false
   675  
   676  	// Setup reader/writers
   677  	defaultInputReader = bytes.NewBufferString("")
   678  	defaultInputWriter = new(bytes.Buffer)
   679  
   680  	// Setup answers
   681  	testInputResponse = nil
   682  	testInputResponseMap = answers
   683  
   684  	// Return the cleanup
   685  	return func() {
   686  		test = true
   687  		testInputResponseMap = nil
   688  	}
   689  }
   690  
   691  // testBackendState is used to make a test HTTP server to test a configured
   692  // backend. This returns the complete state that can be saved. Use
   693  // `testStateFileRemote` to write the returned state.
   694  //
   695  // When using this function, the configuration fixture for the test must
   696  // include an empty configuration block for the HTTP backend, like this:
   697  //
   698  // terraform {
   699  //   backend "http" {
   700  //   }
   701  // }
   702  //
   703  // If such a block isn't present, or if it isn't empty, then an error will
   704  // be returned about the backend configuration having changed and that
   705  // "terraform init" must be run, since the test backend config cache created
   706  // by this function contains the hash for an empty configuration.
   707  func testBackendState(t *testing.T, s *terraform.State, c int) (*terraform.State, *httptest.Server) {
   708  	t.Helper()
   709  
   710  	var b64md5 string
   711  	buf := bytes.NewBuffer(nil)
   712  
   713  	cb := func(resp http.ResponseWriter, req *http.Request) {
   714  		if req.Method == "PUT" {
   715  			resp.WriteHeader(c)
   716  			return
   717  		}
   718  		if s == nil {
   719  			resp.WriteHeader(404)
   720  			return
   721  		}
   722  
   723  		resp.Header().Set("Content-MD5", b64md5)
   724  		resp.Write(buf.Bytes())
   725  	}
   726  
   727  	// If a state was given, make sure we calculate the proper b64md5
   728  	if s != nil {
   729  		enc := json.NewEncoder(buf)
   730  		if err := enc.Encode(s); err != nil {
   731  			t.Fatalf("err: %v", err)
   732  		}
   733  		md5 := md5.Sum(buf.Bytes())
   734  		b64md5 = base64.StdEncoding.EncodeToString(md5[:16])
   735  	}
   736  
   737  	srv := httptest.NewServer(http.HandlerFunc(cb))
   738  
   739  	backendConfig := &configs.Backend{
   740  		Type:   "http",
   741  		Config: configs.SynthBody("<testBackendState>", map[string]cty.Value{}),
   742  	}
   743  	b := backendInit.Backend("http")()
   744  	configSchema := b.ConfigSchema()
   745  	hash := backendConfig.Hash(configSchema)
   746  
   747  	state := terraform.NewState()
   748  	state.Backend = &terraform.BackendState{
   749  		Type:      "http",
   750  		ConfigRaw: json.RawMessage(fmt.Sprintf(`{"address":%q}`, srv.URL)),
   751  		Hash:      uint64(hash),
   752  	}
   753  
   754  	return state, srv
   755  }
   756  
   757  // testRemoteState is used to make a test HTTP server to return a given
   758  // state file that can be used for testing legacy remote state.
   759  //
   760  // The return values are a *terraform.State instance that should be written
   761  // as the "data state" (really: backend state) and the server that the
   762  // returned data state refers to.
   763  func testRemoteState(t *testing.T, s *states.State, c int) (*terraform.State, *httptest.Server) {
   764  	t.Helper()
   765  
   766  	var b64md5 string
   767  	buf := bytes.NewBuffer(nil)
   768  
   769  	cb := func(resp http.ResponseWriter, req *http.Request) {
   770  		if req.Method == "PUT" {
   771  			resp.WriteHeader(c)
   772  			return
   773  		}
   774  		if s == nil {
   775  			resp.WriteHeader(404)
   776  			return
   777  		}
   778  
   779  		resp.Header().Set("Content-MD5", b64md5)
   780  		resp.Write(buf.Bytes())
   781  	}
   782  
   783  	retState := terraform.NewState()
   784  
   785  	srv := httptest.NewServer(http.HandlerFunc(cb))
   786  	b := &terraform.BackendState{
   787  		Type: "http",
   788  	}
   789  	b.SetConfig(cty.ObjectVal(map[string]cty.Value{
   790  		"address": cty.StringVal(srv.URL),
   791  	}), &configschema.Block{
   792  		Attributes: map[string]*configschema.Attribute{
   793  			"address": {
   794  				Type:     cty.String,
   795  				Required: true,
   796  			},
   797  		},
   798  	})
   799  	retState.Backend = b
   800  
   801  	if s != nil {
   802  		err := statefile.Write(&statefile.File{State: s}, buf)
   803  		if err != nil {
   804  			t.Fatalf("failed to write initial state: %v", err)
   805  		}
   806  	}
   807  
   808  	return retState, srv
   809  }
   810  
   811  // testlockState calls a separate process to the lock the state file at path.
   812  // deferFunc should be called in the caller to properly unlock the file.
   813  // Since many tests change the working directory, the sourcedir argument must be
   814  // supplied to locate the statelocker.go source.
   815  func testLockState(sourceDir, path string) (func(), error) {
   816  	// build and run the binary ourselves so we can quickly terminate it for cleanup
   817  	buildDir, err := ioutil.TempDir(testingDir, "locker")
   818  	if err != nil {
   819  		return nil, err
   820  	}
   821  	cleanFunc := func() {
   822  		os.RemoveAll(buildDir)
   823  	}
   824  
   825  	source := filepath.Join(sourceDir, "statelocker.go")
   826  	lockBin := filepath.Join(buildDir, "statelocker")
   827  
   828  	cmd := exec.Command("go", "build", "-mod=vendor", "-o", lockBin, source)
   829  	cmd.Dir = filepath.Dir(sourceDir)
   830  
   831  	out, err := cmd.CombinedOutput()
   832  	if err != nil {
   833  		cleanFunc()
   834  		return nil, fmt.Errorf("%s %s", err, out)
   835  	}
   836  
   837  	locker := exec.Command(lockBin, path)
   838  	pr, pw, err := os.Pipe()
   839  	if err != nil {
   840  		cleanFunc()
   841  		return nil, err
   842  	}
   843  	defer pr.Close()
   844  	defer pw.Close()
   845  	locker.Stderr = pw
   846  	locker.Stdout = pw
   847  
   848  	if err := locker.Start(); err != nil {
   849  		return nil, err
   850  	}
   851  	deferFunc := func() {
   852  		cleanFunc()
   853  		locker.Process.Signal(syscall.SIGTERM)
   854  		locker.Wait()
   855  	}
   856  
   857  	// wait for the process to lock
   858  	buf := make([]byte, 1024)
   859  	n, err := pr.Read(buf)
   860  	if err != nil {
   861  		return deferFunc, fmt.Errorf("read from statelocker returned: %s", err)
   862  	}
   863  
   864  	output := string(buf[:n])
   865  	if !strings.HasPrefix(output, "LOCKID") {
   866  		return deferFunc, fmt.Errorf("statelocker wrote: %s", string(buf[:n]))
   867  	}
   868  	return deferFunc, nil
   869  }
   870  
   871  // normalizeJSON removes all insignificant whitespace from the given JSON buffer
   872  // and returns it as a string for easier comparison.
   873  func normalizeJSON(t *testing.T, src []byte) string {
   874  	t.Helper()
   875  	var buf bytes.Buffer
   876  	err := json.Compact(&buf, src)
   877  	if err != nil {
   878  		t.Fatalf("error normalizing JSON: %s", err)
   879  	}
   880  	return buf.String()
   881  }
   882  
   883  func mustResourceAddr(s string) addrs.AbsResource {
   884  	addr, diags := addrs.ParseAbsResourceStr(s)
   885  	if diags.HasErrors() {
   886  		panic(diags.Err())
   887  	}
   888  	return addr
   889  }