github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/init_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"encoding/json"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"log"
    10  	"os"
    11  	"path/filepath"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/davecgh/go-spew/spew"
    16  	"github.com/google/go-cmp/cmp"
    17  	"github.com/mitchellh/cli"
    18  	"github.com/zclconf/go-cty/cty"
    19  
    20  	"github.com/hashicorp/go-version"
    21  
    22  	"github.com/hashicorp/terraform/internal/addrs"
    23  	"github.com/hashicorp/terraform/internal/configs"
    24  	"github.com/hashicorp/terraform/internal/configs/configschema"
    25  	"github.com/hashicorp/terraform/internal/depsfile"
    26  	"github.com/hashicorp/terraform/internal/getproviders"
    27  	"github.com/hashicorp/terraform/internal/providercache"
    28  	"github.com/hashicorp/terraform/internal/states"
    29  	"github.com/hashicorp/terraform/internal/states/statefile"
    30  	"github.com/hashicorp/terraform/internal/states/statemgr"
    31  )
    32  
    33  func TestInit_empty(t *testing.T) {
    34  	// Create a temporary working directory that is empty
    35  	td := t.TempDir()
    36  	os.MkdirAll(td, 0755)
    37  	defer testChdir(t, td)()
    38  
    39  	ui := new(cli.MockUi)
    40  	view, _ := testView(t)
    41  	c := &InitCommand{
    42  		Meta: Meta{
    43  			testingOverrides: metaOverridesForProvider(testProvider()),
    44  			Ui:               ui,
    45  			View:             view,
    46  		},
    47  	}
    48  
    49  	args := []string{}
    50  	if code := c.Run(args); code != 0 {
    51  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
    52  	}
    53  }
    54  
    55  func TestInit_multipleArgs(t *testing.T) {
    56  	// Create a temporary working directory that is empty
    57  	td := t.TempDir()
    58  	os.MkdirAll(td, 0755)
    59  	defer testChdir(t, td)()
    60  
    61  	ui := new(cli.MockUi)
    62  	view, _ := testView(t)
    63  	c := &InitCommand{
    64  		Meta: Meta{
    65  			testingOverrides: metaOverridesForProvider(testProvider()),
    66  			Ui:               ui,
    67  			View:             view,
    68  		},
    69  	}
    70  
    71  	args := []string{
    72  		"bad",
    73  		"bad",
    74  	}
    75  	if code := c.Run(args); code != 1 {
    76  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
    77  	}
    78  }
    79  
    80  func TestInit_fromModule_cwdDest(t *testing.T) {
    81  	// Create a temporary working directory that is empty
    82  	td := t.TempDir()
    83  	os.MkdirAll(td, os.ModePerm)
    84  	defer testChdir(t, td)()
    85  
    86  	ui := new(cli.MockUi)
    87  	view, _ := testView(t)
    88  	c := &InitCommand{
    89  		Meta: Meta{
    90  			testingOverrides: metaOverridesForProvider(testProvider()),
    91  			Ui:               ui,
    92  			View:             view,
    93  		},
    94  	}
    95  
    96  	args := []string{
    97  		"-from-module=" + testFixturePath("init"),
    98  	}
    99  	if code := c.Run(args); code != 0 {
   100  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   101  	}
   102  
   103  	if _, err := os.Stat(filepath.Join(td, "hello.tf")); err != nil {
   104  		t.Fatalf("err: %s", err)
   105  	}
   106  }
   107  
   108  // https://github.com/hashicorp/terraform/issues/518
   109  func TestInit_fromModule_dstInSrc(t *testing.T) {
   110  	dir := t.TempDir()
   111  	if err := os.MkdirAll(dir, 0755); err != nil {
   112  		t.Fatalf("err: %s", err)
   113  	}
   114  
   115  	// Change to the temporary directory
   116  	cwd, err := os.Getwd()
   117  	if err != nil {
   118  		t.Fatalf("err: %s", err)
   119  	}
   120  	if err := os.Chdir(dir); err != nil {
   121  		t.Fatalf("err: %s", err)
   122  	}
   123  	defer os.Chdir(cwd)
   124  
   125  	if err := os.Mkdir("foo", os.ModePerm); err != nil {
   126  		t.Fatal(err)
   127  	}
   128  
   129  	if _, err := os.Create("issue518.tf"); err != nil {
   130  		t.Fatalf("err: %s", err)
   131  	}
   132  
   133  	if err := os.Chdir("foo"); err != nil {
   134  		t.Fatalf("err: %s", err)
   135  	}
   136  
   137  	ui := new(cli.MockUi)
   138  	view, _ := testView(t)
   139  	c := &InitCommand{
   140  		Meta: Meta{
   141  			testingOverrides: metaOverridesForProvider(testProvider()),
   142  			Ui:               ui,
   143  			View:             view,
   144  		},
   145  	}
   146  
   147  	args := []string{
   148  		"-from-module=./..",
   149  	}
   150  	if code := c.Run(args); code != 0 {
   151  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   152  	}
   153  
   154  	if _, err := os.Stat(filepath.Join(dir, "foo", "issue518.tf")); err != nil {
   155  		t.Fatalf("err: %s", err)
   156  	}
   157  }
   158  
   159  func TestInit_get(t *testing.T) {
   160  	// Create a temporary working directory that is empty
   161  	td := t.TempDir()
   162  	testCopyDir(t, testFixturePath("init-get"), td)
   163  	defer testChdir(t, td)()
   164  
   165  	ui := new(cli.MockUi)
   166  	view, _ := testView(t)
   167  	c := &InitCommand{
   168  		Meta: Meta{
   169  			testingOverrides: metaOverridesForProvider(testProvider()),
   170  			Ui:               ui,
   171  			View:             view,
   172  		},
   173  	}
   174  
   175  	args := []string{}
   176  	if code := c.Run(args); code != 0 {
   177  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   178  	}
   179  
   180  	// Check output
   181  	output := ui.OutputWriter.String()
   182  	if !strings.Contains(output, "foo in foo") {
   183  		t.Fatalf("doesn't look like we installed module 'foo': %s", output)
   184  	}
   185  }
   186  
   187  func TestInit_getUpgradeModules(t *testing.T) {
   188  	// Create a temporary working directory that is empty
   189  	td := t.TempDir()
   190  	testCopyDir(t, testFixturePath("init-get"), td)
   191  	defer testChdir(t, td)()
   192  
   193  	ui := new(cli.MockUi)
   194  	view, _ := testView(t)
   195  	c := &InitCommand{
   196  		Meta: Meta{
   197  			testingOverrides: metaOverridesForProvider(testProvider()),
   198  			Ui:               ui,
   199  			View:             view,
   200  		},
   201  	}
   202  
   203  	args := []string{
   204  		"-get=true",
   205  		"-upgrade",
   206  	}
   207  	if code := c.Run(args); code != 0 {
   208  		t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String())
   209  	}
   210  
   211  	// Check output
   212  	output := ui.OutputWriter.String()
   213  	if !strings.Contains(output, "Upgrading modules...") {
   214  		t.Fatalf("doesn't look like get upgrade: %s", output)
   215  	}
   216  }
   217  
   218  func TestInit_backend(t *testing.T) {
   219  	// Create a temporary working directory that is empty
   220  	td := t.TempDir()
   221  	testCopyDir(t, testFixturePath("init-backend"), td)
   222  	defer testChdir(t, td)()
   223  
   224  	ui := new(cli.MockUi)
   225  	view, _ := testView(t)
   226  	c := &InitCommand{
   227  		Meta: Meta{
   228  			testingOverrides: metaOverridesForProvider(testProvider()),
   229  			Ui:               ui,
   230  			View:             view,
   231  		},
   232  	}
   233  
   234  	args := []string{}
   235  	if code := c.Run(args); code != 0 {
   236  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   237  	}
   238  
   239  	if _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)); err != nil {
   240  		t.Fatalf("err: %s", err)
   241  	}
   242  }
   243  
   244  func TestInit_backendUnset(t *testing.T) {
   245  	// Create a temporary working directory that is empty
   246  	td := t.TempDir()
   247  	testCopyDir(t, testFixturePath("init-backend"), td)
   248  	defer testChdir(t, td)()
   249  
   250  	{
   251  		log.Printf("[TRACE] TestInit_backendUnset: beginning first init")
   252  
   253  		ui := cli.NewMockUi()
   254  		view, _ := testView(t)
   255  		c := &InitCommand{
   256  			Meta: Meta{
   257  				testingOverrides: metaOverridesForProvider(testProvider()),
   258  				Ui:               ui,
   259  				View:             view,
   260  			},
   261  		}
   262  
   263  		// Init
   264  		args := []string{}
   265  		if code := c.Run(args); code != 0 {
   266  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   267  		}
   268  		log.Printf("[TRACE] TestInit_backendUnset: first init complete")
   269  		t.Logf("First run output:\n%s", ui.OutputWriter.String())
   270  		t.Logf("First run errors:\n%s", ui.ErrorWriter.String())
   271  
   272  		if _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)); err != nil {
   273  			t.Fatalf("err: %s", err)
   274  		}
   275  	}
   276  
   277  	{
   278  		log.Printf("[TRACE] TestInit_backendUnset: beginning second init")
   279  
   280  		// Unset
   281  		if err := ioutil.WriteFile("main.tf", []byte(""), 0644); err != nil {
   282  			t.Fatalf("err: %s", err)
   283  		}
   284  
   285  		ui := cli.NewMockUi()
   286  		view, _ := testView(t)
   287  		c := &InitCommand{
   288  			Meta: Meta{
   289  				testingOverrides: metaOverridesForProvider(testProvider()),
   290  				Ui:               ui,
   291  				View:             view,
   292  			},
   293  		}
   294  
   295  		args := []string{"-force-copy"}
   296  		if code := c.Run(args); code != 0 {
   297  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   298  		}
   299  		log.Printf("[TRACE] TestInit_backendUnset: second init complete")
   300  		t.Logf("Second run output:\n%s", ui.OutputWriter.String())
   301  		t.Logf("Second run errors:\n%s", ui.ErrorWriter.String())
   302  
   303  		s := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   304  		if !s.Backend.Empty() {
   305  			t.Fatal("should not have backend config")
   306  		}
   307  	}
   308  }
   309  
   310  func TestInit_backendConfigFile(t *testing.T) {
   311  	// Create a temporary working directory that is empty
   312  	td := t.TempDir()
   313  	testCopyDir(t, testFixturePath("init-backend-config-file"), td)
   314  	defer testChdir(t, td)()
   315  
   316  	t.Run("good-config-file", func(t *testing.T) {
   317  		ui := new(cli.MockUi)
   318  		view, _ := testView(t)
   319  		c := &InitCommand{
   320  			Meta: Meta{
   321  				testingOverrides: metaOverridesForProvider(testProvider()),
   322  				Ui:               ui,
   323  				View:             view,
   324  			},
   325  		}
   326  		args := []string{"-backend-config", "input.config"}
   327  		if code := c.Run(args); code != 0 {
   328  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   329  		}
   330  
   331  		// Read our saved backend config and verify we have our settings
   332  		state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   333  		if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   334  			t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   335  		}
   336  	})
   337  
   338  	// the backend config file must not be a full terraform block
   339  	t.Run("full-backend-config-file", func(t *testing.T) {
   340  		ui := new(cli.MockUi)
   341  		view, _ := testView(t)
   342  		c := &InitCommand{
   343  			Meta: Meta{
   344  				testingOverrides: metaOverridesForProvider(testProvider()),
   345  				Ui:               ui,
   346  				View:             view,
   347  			},
   348  		}
   349  		args := []string{"-backend-config", "backend.config"}
   350  		if code := c.Run(args); code != 1 {
   351  			t.Fatalf("expected error, got success\n")
   352  		}
   353  		if !strings.Contains(ui.ErrorWriter.String(), "Unsupported block type") {
   354  			t.Fatalf("wrong error: %s", ui.ErrorWriter)
   355  		}
   356  	})
   357  
   358  	// the backend config file must match the schema for the backend
   359  	t.Run("invalid-config-file", func(t *testing.T) {
   360  		ui := new(cli.MockUi)
   361  		view, _ := testView(t)
   362  		c := &InitCommand{
   363  			Meta: Meta{
   364  				testingOverrides: metaOverridesForProvider(testProvider()),
   365  				Ui:               ui,
   366  				View:             view,
   367  			},
   368  		}
   369  		args := []string{"-backend-config", "invalid.config"}
   370  		if code := c.Run(args); code != 1 {
   371  			t.Fatalf("expected error, got success\n")
   372  		}
   373  		if !strings.Contains(ui.ErrorWriter.String(), "Unsupported argument") {
   374  			t.Fatalf("wrong error: %s", ui.ErrorWriter)
   375  		}
   376  	})
   377  
   378  	// missing file is an error
   379  	t.Run("missing-config-file", func(t *testing.T) {
   380  		ui := new(cli.MockUi)
   381  		view, _ := testView(t)
   382  		c := &InitCommand{
   383  			Meta: Meta{
   384  				testingOverrides: metaOverridesForProvider(testProvider()),
   385  				Ui:               ui,
   386  				View:             view,
   387  			},
   388  		}
   389  		args := []string{"-backend-config", "missing.config"}
   390  		if code := c.Run(args); code != 1 {
   391  			t.Fatalf("expected error, got success\n")
   392  		}
   393  		if !strings.Contains(ui.ErrorWriter.String(), "Failed to read file") {
   394  			t.Fatalf("wrong error: %s", ui.ErrorWriter)
   395  		}
   396  	})
   397  
   398  	// blank filename clears the backend config
   399  	t.Run("blank-config-file", func(t *testing.T) {
   400  		ui := new(cli.MockUi)
   401  		view, _ := testView(t)
   402  		c := &InitCommand{
   403  			Meta: Meta{
   404  				testingOverrides: metaOverridesForProvider(testProvider()),
   405  				Ui:               ui,
   406  				View:             view,
   407  			},
   408  		}
   409  		args := []string{"-backend-config=", "-migrate-state"}
   410  		if code := c.Run(args); code != 0 {
   411  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   412  		}
   413  
   414  		// Read our saved backend config and verify the backend config is empty
   415  		state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   416  		if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":null,"workspace_dir":null}`; got != want {
   417  			t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   418  		}
   419  	})
   420  
   421  	// simulate the local backend having a required field which is not
   422  	// specified in the override file
   423  	t.Run("required-argument", func(t *testing.T) {
   424  		c := &InitCommand{}
   425  		schema := &configschema.Block{
   426  			Attributes: map[string]*configschema.Attribute{
   427  				"path": {
   428  					Type:     cty.String,
   429  					Optional: true,
   430  				},
   431  				"workspace_dir": {
   432  					Type:     cty.String,
   433  					Required: true,
   434  				},
   435  			},
   436  		}
   437  		flagConfigExtra := newRawFlags("-backend-config")
   438  		flagConfigExtra.Set("input.config")
   439  		_, diags := c.backendConfigOverrideBody(flagConfigExtra, schema)
   440  		if len(diags) != 0 {
   441  			t.Errorf("expected no diags, got: %s", diags.Err())
   442  		}
   443  	})
   444  }
   445  
   446  func TestInit_backendConfigFilePowershellConfusion(t *testing.T) {
   447  	// Create a temporary working directory that is empty
   448  	td := t.TempDir()
   449  	testCopyDir(t, testFixturePath("init-backend-config-file"), td)
   450  	defer testChdir(t, td)()
   451  
   452  	ui := new(cli.MockUi)
   453  	view, _ := testView(t)
   454  	c := &InitCommand{
   455  		Meta: Meta{
   456  			testingOverrides: metaOverridesForProvider(testProvider()),
   457  			Ui:               ui,
   458  			View:             view,
   459  		},
   460  	}
   461  
   462  	// SUBTLE: when using -flag=value with Powershell, unquoted values are
   463  	// broken into separate arguments. This results in the init command
   464  	// interpreting the flags as an empty backend-config setting (which is
   465  	// semantically valid!) followed by a custom configuration path.
   466  	//
   467  	// Adding the "=" here forces this codepath to be checked, and it should
   468  	// result in an early exit with a diagnostic that the provided
   469  	// configuration file is not a diretory.
   470  	args := []string{"-backend-config=", "./input.config"}
   471  	if code := c.Run(args); code != 1 {
   472  		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
   473  	}
   474  
   475  	output := ui.ErrorWriter.String()
   476  	if got, want := output, `Too many command line arguments`; !strings.Contains(got, want) {
   477  		t.Fatalf("wrong output\ngot:\n%s\n\nwant: message containing %q", got, want)
   478  	}
   479  }
   480  
   481  func TestInit_backendReconfigure(t *testing.T) {
   482  	// Create a temporary working directory that is empty
   483  	td := t.TempDir()
   484  	testCopyDir(t, testFixturePath("init-backend"), td)
   485  	defer testChdir(t, td)()
   486  
   487  	providerSource, close := newMockProviderSource(t, map[string][]string{
   488  		"hashicorp/test": {"1.2.3"},
   489  	})
   490  	defer close()
   491  
   492  	ui := new(cli.MockUi)
   493  	view, _ := testView(t)
   494  	c := &InitCommand{
   495  		Meta: Meta{
   496  			testingOverrides: metaOverridesForProvider(testProvider()),
   497  			ProviderSource:   providerSource,
   498  			Ui:               ui,
   499  			View:             view,
   500  		},
   501  	}
   502  
   503  	// create some state, so the backend has something to migrate.
   504  	f, err := os.Create("foo") // this is the path" in the backend config
   505  	if err != nil {
   506  		t.Fatalf("err: %s", err)
   507  	}
   508  	err = writeStateForTesting(testState(), f)
   509  	f.Close()
   510  	if err != nil {
   511  		t.Fatalf("err: %s", err)
   512  	}
   513  
   514  	args := []string{}
   515  	if code := c.Run(args); code != 0 {
   516  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   517  	}
   518  
   519  	// now run init again, changing the path.
   520  	// The -reconfigure flag prevents init from migrating
   521  	// Without -reconfigure, the test fails since the backend asks for input on migrating state
   522  	args = []string{"-reconfigure", "-backend-config", "path=changed"}
   523  	if code := c.Run(args); code != 0 {
   524  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   525  	}
   526  }
   527  
   528  func TestInit_backendConfigFileChange(t *testing.T) {
   529  	// Create a temporary working directory that is empty
   530  	td := t.TempDir()
   531  	testCopyDir(t, testFixturePath("init-backend-config-file-change"), td)
   532  	defer testChdir(t, td)()
   533  
   534  	ui := new(cli.MockUi)
   535  	view, _ := testView(t)
   536  	c := &InitCommand{
   537  		Meta: Meta{
   538  			testingOverrides: metaOverridesForProvider(testProvider()),
   539  			Ui:               ui,
   540  			View:             view,
   541  		},
   542  	}
   543  
   544  	args := []string{"-backend-config", "input.config", "-migrate-state"}
   545  	if code := c.Run(args); code != 0 {
   546  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   547  	}
   548  
   549  	// Read our saved backend config and verify we have our settings
   550  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   551  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   552  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   553  	}
   554  }
   555  
   556  func TestInit_backendMigrateWhileLocked(t *testing.T) {
   557  	// Create a temporary working directory that is empty
   558  	td := t.TempDir()
   559  	testCopyDir(t, testFixturePath("init-backend-migrate-while-locked"), td)
   560  	defer testChdir(t, td)()
   561  
   562  	providerSource, close := newMockProviderSource(t, map[string][]string{
   563  		"hashicorp/test": {"1.2.3"},
   564  	})
   565  	defer close()
   566  
   567  	ui := new(cli.MockUi)
   568  	view, _ := testView(t)
   569  	c := &InitCommand{
   570  		Meta: Meta{
   571  			testingOverrides: metaOverridesForProvider(testProvider()),
   572  			ProviderSource:   providerSource,
   573  			Ui:               ui,
   574  			View:             view,
   575  		},
   576  	}
   577  
   578  	// Create some state, so the backend has something to migrate from
   579  	f, err := os.Create("local-state.tfstate")
   580  	if err != nil {
   581  		t.Fatalf("err: %s", err)
   582  	}
   583  	err = writeStateForTesting(testState(), f)
   584  	f.Close()
   585  	if err != nil {
   586  		t.Fatalf("err: %s", err)
   587  	}
   588  
   589  	// Lock the source state
   590  	unlock, err := testLockState(t, testDataDir, "local-state.tfstate")
   591  	if err != nil {
   592  		t.Fatal(err)
   593  	}
   594  	defer unlock()
   595  
   596  	// Attempt to migrate
   597  	args := []string{"-backend-config", "input.config", "-migrate-state", "-force-copy"}
   598  	if code := c.Run(args); code == 0 {
   599  		t.Fatalf("expected nonzero exit code: %s", ui.OutputWriter.String())
   600  	}
   601  
   602  	// Disabling locking should work
   603  	args = []string{"-backend-config", "input.config", "-migrate-state", "-force-copy", "-lock=false"}
   604  	if code := c.Run(args); code != 0 {
   605  		t.Fatalf("expected zero exit code, got %d: %s", code, ui.ErrorWriter.String())
   606  	}
   607  }
   608  
   609  func TestInit_backendConfigFileChangeWithExistingState(t *testing.T) {
   610  	// Create a temporary working directory that is empty
   611  	td := t.TempDir()
   612  	testCopyDir(t, testFixturePath("init-backend-config-file-change-migrate-existing"), td)
   613  	defer testChdir(t, td)()
   614  
   615  	ui := new(cli.MockUi)
   616  	c := &InitCommand{
   617  		Meta: Meta{
   618  			testingOverrides: metaOverridesForProvider(testProvider()),
   619  			Ui:               ui,
   620  		},
   621  	}
   622  
   623  	oldState := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   624  
   625  	// we deliberately do not provide the answer for backend-migrate-copy-to-empty to trigger error
   626  	args := []string{"-migrate-state", "-backend-config", "input.config", "-input=true"}
   627  	if code := c.Run(args); code == 0 {
   628  		t.Fatal("expected error")
   629  	}
   630  
   631  	// Read our backend config and verify new settings are not saved
   632  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   633  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"local-state.tfstate"}`; got != want {
   634  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   635  	}
   636  
   637  	// without changing config, hash should not change
   638  	if oldState.Backend.Hash != state.Backend.Hash {
   639  		t.Errorf("backend hash should not have changed\ngot:  %d\nwant: %d", state.Backend.Hash, oldState.Backend.Hash)
   640  	}
   641  }
   642  
   643  func TestInit_backendConfigKV(t *testing.T) {
   644  	// Create a temporary working directory that is empty
   645  	td := t.TempDir()
   646  	testCopyDir(t, testFixturePath("init-backend-config-kv"), td)
   647  	defer testChdir(t, td)()
   648  
   649  	ui := new(cli.MockUi)
   650  	view, _ := testView(t)
   651  	c := &InitCommand{
   652  		Meta: Meta{
   653  			testingOverrides: metaOverridesForProvider(testProvider()),
   654  			Ui:               ui,
   655  			View:             view,
   656  		},
   657  	}
   658  
   659  	args := []string{"-backend-config", "path=hello"}
   660  	if code := c.Run(args); code != 0 {
   661  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   662  	}
   663  
   664  	// Read our saved backend config and verify we have our settings
   665  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   666  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   667  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   668  	}
   669  }
   670  
   671  func TestInit_backendConfigKVReInit(t *testing.T) {
   672  	// Create a temporary working directory that is empty
   673  	td := t.TempDir()
   674  	testCopyDir(t, testFixturePath("init-backend-config-kv"), td)
   675  	defer testChdir(t, td)()
   676  
   677  	ui := new(cli.MockUi)
   678  	view, _ := testView(t)
   679  	c := &InitCommand{
   680  		Meta: Meta{
   681  			testingOverrides: metaOverridesForProvider(testProvider()),
   682  			Ui:               ui,
   683  			View:             view,
   684  		},
   685  	}
   686  
   687  	args := []string{"-backend-config", "path=test"}
   688  	if code := c.Run(args); code != 0 {
   689  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   690  	}
   691  
   692  	ui = new(cli.MockUi)
   693  	c = &InitCommand{
   694  		Meta: Meta{
   695  			testingOverrides: metaOverridesForProvider(testProvider()),
   696  			Ui:               ui,
   697  			View:             view,
   698  		},
   699  	}
   700  
   701  	// a second init should require no changes, nor should it change the backend.
   702  	args = []string{"-input=false"}
   703  	if code := c.Run(args); code != 0 {
   704  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   705  	}
   706  
   707  	// make sure the backend is configured how we expect
   708  	configState := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   709  	cfg := map[string]interface{}{}
   710  	if err := json.Unmarshal(configState.Backend.ConfigRaw, &cfg); err != nil {
   711  		t.Fatal(err)
   712  	}
   713  	if cfg["path"] != "test" {
   714  		t.Fatalf(`expected backend path="test", got path="%v"`, cfg["path"])
   715  	}
   716  
   717  	// override the -backend-config options by settings
   718  	args = []string{"-input=false", "-backend-config", "", "-migrate-state"}
   719  	if code := c.Run(args); code != 0 {
   720  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   721  	}
   722  
   723  	// make sure the backend is configured how we expect
   724  	configState = testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   725  	cfg = map[string]interface{}{}
   726  	if err := json.Unmarshal(configState.Backend.ConfigRaw, &cfg); err != nil {
   727  		t.Fatal(err)
   728  	}
   729  	if cfg["path"] != nil {
   730  		t.Fatalf(`expected backend path="<nil>", got path="%v"`, cfg["path"])
   731  	}
   732  }
   733  
   734  func TestInit_backendConfigKVReInitWithConfigDiff(t *testing.T) {
   735  	// Create a temporary working directory that is empty
   736  	td := t.TempDir()
   737  	testCopyDir(t, testFixturePath("init-backend"), td)
   738  	defer testChdir(t, td)()
   739  
   740  	ui := new(cli.MockUi)
   741  	view, _ := testView(t)
   742  	c := &InitCommand{
   743  		Meta: Meta{
   744  			testingOverrides: metaOverridesForProvider(testProvider()),
   745  			Ui:               ui,
   746  			View:             view,
   747  		},
   748  	}
   749  
   750  	args := []string{"-input=false"}
   751  	if code := c.Run(args); code != 0 {
   752  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   753  	}
   754  
   755  	ui = new(cli.MockUi)
   756  	c = &InitCommand{
   757  		Meta: Meta{
   758  			testingOverrides: metaOverridesForProvider(testProvider()),
   759  			Ui:               ui,
   760  			View:             view,
   761  		},
   762  	}
   763  
   764  	// a second init with identical config should require no changes, nor
   765  	// should it change the backend.
   766  	args = []string{"-input=false", "-backend-config", "path=foo"}
   767  	if code := c.Run(args); code != 0 {
   768  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   769  	}
   770  
   771  	// make sure the backend is configured how we expect
   772  	configState := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   773  	cfg := map[string]interface{}{}
   774  	if err := json.Unmarshal(configState.Backend.ConfigRaw, &cfg); err != nil {
   775  		t.Fatal(err)
   776  	}
   777  	if cfg["path"] != "foo" {
   778  		t.Fatalf(`expected backend path="foo", got path="%v"`, cfg["foo"])
   779  	}
   780  }
   781  
   782  func TestInit_backendCli_no_config_block(t *testing.T) {
   783  	// Create a temporary working directory that is empty
   784  	td := t.TempDir()
   785  	testCopyDir(t, testFixturePath("init"), td)
   786  	defer testChdir(t, td)()
   787  
   788  	ui := new(cli.MockUi)
   789  	view, _ := testView(t)
   790  	c := &InitCommand{
   791  		Meta: Meta{
   792  			testingOverrides: metaOverridesForProvider(testProvider()),
   793  			Ui:               ui,
   794  			View:             view,
   795  		},
   796  	}
   797  
   798  	args := []string{"-backend-config", "path=test"}
   799  	if code := c.Run(args); code != 0 {
   800  		t.Fatalf("got exit status %d; want 0\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
   801  	}
   802  
   803  	errMsg := ui.ErrorWriter.String()
   804  	if !strings.Contains(errMsg, "Warning: Missing backend configuration") {
   805  		t.Fatal("expected missing backend block warning, got", errMsg)
   806  	}
   807  }
   808  
   809  func TestInit_backendReinitWithExtra(t *testing.T) {
   810  	td := t.TempDir()
   811  	testCopyDir(t, testFixturePath("init-backend-empty"), td)
   812  	defer testChdir(t, td)()
   813  
   814  	m := testMetaBackend(t, nil)
   815  	opts := &BackendOpts{
   816  		ConfigOverride: configs.SynthBody("synth", map[string]cty.Value{
   817  			"path": cty.StringVal("hello"),
   818  		}),
   819  		Init: true,
   820  	}
   821  
   822  	_, cHash, err := m.backendConfig(opts)
   823  	if err != nil {
   824  		t.Fatal(err)
   825  	}
   826  
   827  	ui := new(cli.MockUi)
   828  	view, _ := testView(t)
   829  	c := &InitCommand{
   830  		Meta: Meta{
   831  			testingOverrides: metaOverridesForProvider(testProvider()),
   832  			Ui:               ui,
   833  			View:             view,
   834  		},
   835  	}
   836  
   837  	args := []string{"-backend-config", "path=hello"}
   838  	if code := c.Run(args); code != 0 {
   839  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   840  	}
   841  
   842  	// Read our saved backend config and verify we have our settings
   843  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   844  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   845  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   846  	}
   847  
   848  	if state.Backend.Hash != uint64(cHash) {
   849  		t.Fatal("mismatched state and config backend hashes")
   850  	}
   851  
   852  	// init again and make sure nothing changes
   853  	if code := c.Run(args); code != 0 {
   854  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   855  	}
   856  	state = testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   857  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   858  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   859  	}
   860  	if state.Backend.Hash != uint64(cHash) {
   861  		t.Fatal("mismatched state and config backend hashes")
   862  	}
   863  }
   864  
   865  // move option from config to -backend-config args
   866  func TestInit_backendReinitConfigToExtra(t *testing.T) {
   867  	td := t.TempDir()
   868  	testCopyDir(t, testFixturePath("init-backend"), td)
   869  	defer testChdir(t, td)()
   870  
   871  	ui := new(cli.MockUi)
   872  	view, _ := testView(t)
   873  	c := &InitCommand{
   874  		Meta: Meta{
   875  			testingOverrides: metaOverridesForProvider(testProvider()),
   876  			Ui:               ui,
   877  			View:             view,
   878  		},
   879  	}
   880  
   881  	if code := c.Run([]string{"-input=false"}); code != 0 {
   882  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   883  	}
   884  
   885  	// Read our saved backend config and verify we have our settings
   886  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   887  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"foo","workspace_dir":null}`; got != want {
   888  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   889  	}
   890  
   891  	backendHash := state.Backend.Hash
   892  
   893  	// init again but remove the path option from the config
   894  	cfg := "terraform {\n  backend \"local\" {}\n}\n"
   895  	if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil {
   896  		t.Fatal(err)
   897  	}
   898  
   899  	// We need a fresh InitCommand here because the old one now has our configuration
   900  	// file cached inside it, so it won't re-read the modification we just made.
   901  	c = &InitCommand{
   902  		Meta: Meta{
   903  			testingOverrides: metaOverridesForProvider(testProvider()),
   904  			Ui:               ui,
   905  			View:             view,
   906  		},
   907  	}
   908  
   909  	args := []string{"-input=false", "-backend-config=path=foo"}
   910  	if code := c.Run(args); code != 0 {
   911  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   912  	}
   913  	state = testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   914  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"foo","workspace_dir":null}`; got != want {
   915  		t.Errorf("wrong config after moving to arg\ngot:  %s\nwant: %s", got, want)
   916  	}
   917  
   918  	if state.Backend.Hash == backendHash {
   919  		t.Fatal("state.Backend.Hash was not updated")
   920  	}
   921  }
   922  
   923  func TestInit_backendCloudInvalidOptions(t *testing.T) {
   924  	// There are various "terraform init" options that are only for
   925  	// traditional backends and not applicable to Terraform Cloud mode.
   926  	// For those, we want to return an explicit error rather than
   927  	// just silently ignoring them, so that users will be aware that
   928  	// Cloud mode has more of an expected "happy path" than the
   929  	// less-vertically-integrated backends do, and to avoid these
   930  	// unapplicable options becoming compatibility constraints for
   931  	// future evolution of Cloud mode.
   932  
   933  	// We use the same starting fixture for all of these tests, but some
   934  	// of them will customize it a bit as part of their work.
   935  	setupTempDir := func(t *testing.T) func() {
   936  		t.Helper()
   937  		td := t.TempDir()
   938  		testCopyDir(t, testFixturePath("init-cloud-simple"), td)
   939  		unChdir := testChdir(t, td)
   940  		return unChdir
   941  	}
   942  
   943  	// Some of the tests need a non-empty placeholder state file to work
   944  	// with.
   945  	fakeState := states.BuildState(func(cb *states.SyncState) {
   946  		// Having a root module output value should be enough for this
   947  		// state file to be considered "non-empty" and thus a candidate
   948  		// for migration.
   949  		cb.SetOutputValue(
   950  			addrs.OutputValue{Name: "a"}.Absolute(addrs.RootModuleInstance),
   951  			cty.True,
   952  			false,
   953  		)
   954  	})
   955  	fakeStateFile := &statefile.File{
   956  		Lineage:          "boop",
   957  		Serial:           4,
   958  		TerraformVersion: version.Must(version.NewVersion("1.0.0")),
   959  		State:            fakeState,
   960  	}
   961  	var fakeStateBuf bytes.Buffer
   962  	err := statefile.WriteForTest(fakeStateFile, &fakeStateBuf)
   963  	if err != nil {
   964  		t.Error(err)
   965  	}
   966  	fakeStateBytes := fakeStateBuf.Bytes()
   967  
   968  	t.Run("-backend-config", func(t *testing.T) {
   969  		defer setupTempDir(t)()
   970  
   971  		// We have -backend-config as a pragmatic way to dynamically set
   972  		// certain settings of backends that tend to vary depending on
   973  		// where Terraform is running, such as AWS authentication profiles
   974  		// that are naturally local only to the machine where Terraform is
   975  		// running. Those needs don't apply to Terraform Cloud, because
   976  		// the remote workspace encapsulates all of the details of how
   977  		// operations and state work in that case, and so the Cloud
   978  		// configuration is only about which workspaces we'll be working
   979  		// with.
   980  		ui := cli.NewMockUi()
   981  		view, _ := testView(t)
   982  		c := &InitCommand{
   983  			Meta: Meta{
   984  				Ui:   ui,
   985  				View: view,
   986  			},
   987  		}
   988  		args := []string{"-backend-config=anything"}
   989  		if code := c.Run(args); code == 0 {
   990  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
   991  		}
   992  
   993  		gotStderr := ui.ErrorWriter.String()
   994  		wantStderr := `
   995  Error: Invalid command-line option
   996  
   997  The -backend-config=... command line option is only for state backends, and
   998  is not applicable to Terraform Cloud-based configurations.
   999  
  1000  To change the set of workspaces associated with this configuration, edit the
  1001  Cloud configuration block in the root module.
  1002  
  1003  `
  1004  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1005  			t.Errorf("wrong error output\n%s", diff)
  1006  		}
  1007  	})
  1008  	t.Run("-reconfigure", func(t *testing.T) {
  1009  		defer setupTempDir(t)()
  1010  
  1011  		// The -reconfigure option was originally imagined as a way to force
  1012  		// skipping state migration when migrating between backends, but it
  1013  		// has a historical flaw that it doesn't work properly when the
  1014  		// initial situation is the implicit local backend with a state file
  1015  		// present. The Terraform Cloud migration path has some additional
  1016  		// steps to take care of more details automatically, and so
  1017  		// -reconfigure doesn't really make sense in that context, particularly
  1018  		// with its design bug with the handling of the implicit local backend.
  1019  		ui := cli.NewMockUi()
  1020  		view, _ := testView(t)
  1021  		c := &InitCommand{
  1022  			Meta: Meta{
  1023  				Ui:   ui,
  1024  				View: view,
  1025  			},
  1026  		}
  1027  		args := []string{"-reconfigure"}
  1028  		if code := c.Run(args); code == 0 {
  1029  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
  1030  		}
  1031  
  1032  		gotStderr := ui.ErrorWriter.String()
  1033  		wantStderr := `
  1034  Error: Invalid command-line option
  1035  
  1036  The -reconfigure option is for in-place reconfiguration of state backends
  1037  only, and is not needed when changing Terraform Cloud settings.
  1038  
  1039  When using Terraform Cloud, initialization automatically activates any new
  1040  Cloud configuration settings.
  1041  
  1042  `
  1043  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1044  			t.Errorf("wrong error output\n%s", diff)
  1045  		}
  1046  	})
  1047  	t.Run("-reconfigure when migrating in", func(t *testing.T) {
  1048  		defer setupTempDir(t)()
  1049  
  1050  		// We have a slightly different error message for the case where we
  1051  		// seem to be trying to migrate to Terraform Cloud with existing
  1052  		// state or explicit backend already present.
  1053  
  1054  		if err := os.WriteFile("terraform.tfstate", fakeStateBytes, 0644); err != nil {
  1055  			t.Fatal(err)
  1056  		}
  1057  
  1058  		ui := cli.NewMockUi()
  1059  		view, _ := testView(t)
  1060  		c := &InitCommand{
  1061  			Meta: Meta{
  1062  				Ui:   ui,
  1063  				View: view,
  1064  			},
  1065  		}
  1066  		args := []string{"-reconfigure"}
  1067  		if code := c.Run(args); code == 0 {
  1068  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
  1069  		}
  1070  
  1071  		gotStderr := ui.ErrorWriter.String()
  1072  		wantStderr := `
  1073  Error: Invalid command-line option
  1074  
  1075  The -reconfigure option is unsupported when migrating to Terraform Cloud,
  1076  because activating Terraform Cloud involves some additional steps.
  1077  
  1078  `
  1079  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1080  			t.Errorf("wrong error output\n%s", diff)
  1081  		}
  1082  	})
  1083  	t.Run("-migrate-state", func(t *testing.T) {
  1084  		defer setupTempDir(t)()
  1085  
  1086  		// In Cloud mode, migrating in or out always proposes migrating state
  1087  		// and changing configuration while staying in cloud mode never migrates
  1088  		// state, so this special option isn't relevant.
  1089  		ui := cli.NewMockUi()
  1090  		view, _ := testView(t)
  1091  		c := &InitCommand{
  1092  			Meta: Meta{
  1093  				Ui:   ui,
  1094  				View: view,
  1095  			},
  1096  		}
  1097  		args := []string{"-migrate-state"}
  1098  		if code := c.Run(args); code == 0 {
  1099  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
  1100  		}
  1101  
  1102  		gotStderr := ui.ErrorWriter.String()
  1103  		wantStderr := `
  1104  Error: Invalid command-line option
  1105  
  1106  The -migrate-state option is for migration between state backends only, and
  1107  is not applicable when using Terraform Cloud.
  1108  
  1109  State storage is handled automatically by Terraform Cloud and so the state
  1110  storage location is not configurable.
  1111  
  1112  `
  1113  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1114  			t.Errorf("wrong error output\n%s", diff)
  1115  		}
  1116  	})
  1117  	t.Run("-migrate-state when migrating in", func(t *testing.T) {
  1118  		defer setupTempDir(t)()
  1119  
  1120  		// We have a slightly different error message for the case where we
  1121  		// seem to be trying to migrate to Terraform Cloud with existing
  1122  		// state or explicit backend already present.
  1123  
  1124  		if err := os.WriteFile("terraform.tfstate", fakeStateBytes, 0644); err != nil {
  1125  			t.Fatal(err)
  1126  		}
  1127  
  1128  		ui := cli.NewMockUi()
  1129  		view, _ := testView(t)
  1130  		c := &InitCommand{
  1131  			Meta: Meta{
  1132  				Ui:   ui,
  1133  				View: view,
  1134  			},
  1135  		}
  1136  		args := []string{"-migrate-state"}
  1137  		if code := c.Run(args); code == 0 {
  1138  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
  1139  		}
  1140  
  1141  		gotStderr := ui.ErrorWriter.String()
  1142  		wantStderr := `
  1143  Error: Invalid command-line option
  1144  
  1145  The -migrate-state option is for migration between state backends only, and
  1146  is not applicable when using Terraform Cloud.
  1147  
  1148  Terraform Cloud migration has additional steps, configured by interactive
  1149  prompts.
  1150  
  1151  `
  1152  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1153  			t.Errorf("wrong error output\n%s", diff)
  1154  		}
  1155  	})
  1156  	t.Run("-force-copy", func(t *testing.T) {
  1157  		defer setupTempDir(t)()
  1158  
  1159  		// In Cloud mode, migrating in or out always proposes migrating state
  1160  		// and changing configuration while staying in cloud mode never migrates
  1161  		// state, so this special option isn't relevant.
  1162  		ui := cli.NewMockUi()
  1163  		view, _ := testView(t)
  1164  		c := &InitCommand{
  1165  			Meta: Meta{
  1166  				Ui:   ui,
  1167  				View: view,
  1168  			},
  1169  		}
  1170  		args := []string{"-force-copy"}
  1171  		if code := c.Run(args); code == 0 {
  1172  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
  1173  		}
  1174  
  1175  		gotStderr := ui.ErrorWriter.String()
  1176  		wantStderr := `
  1177  Error: Invalid command-line option
  1178  
  1179  The -force-copy option is for migration between state backends only, and is
  1180  not applicable when using Terraform Cloud.
  1181  
  1182  State storage is handled automatically by Terraform Cloud and so the state
  1183  storage location is not configurable.
  1184  
  1185  `
  1186  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1187  			t.Errorf("wrong error output\n%s", diff)
  1188  		}
  1189  	})
  1190  	t.Run("-force-copy when migrating in", func(t *testing.T) {
  1191  		defer setupTempDir(t)()
  1192  
  1193  		// We have a slightly different error message for the case where we
  1194  		// seem to be trying to migrate to Terraform Cloud with existing
  1195  		// state or explicit backend already present.
  1196  
  1197  		if err := os.WriteFile("terraform.tfstate", fakeStateBytes, 0644); err != nil {
  1198  			t.Fatal(err)
  1199  		}
  1200  
  1201  		ui := cli.NewMockUi()
  1202  		view, _ := testView(t)
  1203  		c := &InitCommand{
  1204  			Meta: Meta{
  1205  				Ui:   ui,
  1206  				View: view,
  1207  			},
  1208  		}
  1209  		args := []string{"-force-copy"}
  1210  		if code := c.Run(args); code == 0 {
  1211  			t.Fatalf("unexpected success\n%s", ui.OutputWriter.String())
  1212  		}
  1213  
  1214  		gotStderr := ui.ErrorWriter.String()
  1215  		wantStderr := `
  1216  Error: Invalid command-line option
  1217  
  1218  The -force-copy option is for migration between state backends only, and is
  1219  not applicable when using Terraform Cloud.
  1220  
  1221  Terraform Cloud migration has additional steps, configured by interactive
  1222  prompts.
  1223  
  1224  `
  1225  		if diff := cmp.Diff(wantStderr, gotStderr); diff != "" {
  1226  			t.Errorf("wrong error output\n%s", diff)
  1227  		}
  1228  	})
  1229  
  1230  }
  1231  
  1232  // make sure inputFalse stops execution on migrate
  1233  func TestInit_inputFalse(t *testing.T) {
  1234  	td := t.TempDir()
  1235  	testCopyDir(t, testFixturePath("init-backend"), td)
  1236  	defer testChdir(t, td)()
  1237  
  1238  	ui := new(cli.MockUi)
  1239  	view, _ := testView(t)
  1240  	c := &InitCommand{
  1241  		Meta: Meta{
  1242  			testingOverrides: metaOverridesForProvider(testProvider()),
  1243  			Ui:               ui,
  1244  			View:             view,
  1245  		},
  1246  	}
  1247  
  1248  	args := []string{"-input=false", "-backend-config=path=foo"}
  1249  	if code := c.Run(args); code != 0 {
  1250  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  1251  	}
  1252  
  1253  	// write different states for foo and bar
  1254  	fooState := states.BuildState(func(s *states.SyncState) {
  1255  		s.SetOutputValue(
  1256  			addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
  1257  			cty.StringVal("foo"),
  1258  			false, // not sensitive
  1259  		)
  1260  	})
  1261  	if err := statemgr.NewFilesystem("foo").WriteState(fooState); err != nil {
  1262  		t.Fatal(err)
  1263  	}
  1264  	barState := states.BuildState(func(s *states.SyncState) {
  1265  		s.SetOutputValue(
  1266  			addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
  1267  			cty.StringVal("bar"),
  1268  			false, // not sensitive
  1269  		)
  1270  	})
  1271  	if err := statemgr.NewFilesystem("bar").WriteState(barState); err != nil {
  1272  		t.Fatal(err)
  1273  	}
  1274  
  1275  	ui = new(cli.MockUi)
  1276  	c = &InitCommand{
  1277  		Meta: Meta{
  1278  			testingOverrides: metaOverridesForProvider(testProvider()),
  1279  			Ui:               ui,
  1280  			View:             view,
  1281  		},
  1282  	}
  1283  
  1284  	args = []string{"-input=false", "-backend-config=path=bar", "-migrate-state"}
  1285  	if code := c.Run(args); code == 0 {
  1286  		t.Fatal("init should have failed", ui.OutputWriter)
  1287  	}
  1288  
  1289  	errMsg := ui.ErrorWriter.String()
  1290  	if !strings.Contains(errMsg, "interactive input is disabled") {
  1291  		t.Fatal("expected input disabled error, got", errMsg)
  1292  	}
  1293  
  1294  	ui = new(cli.MockUi)
  1295  	c = &InitCommand{
  1296  		Meta: Meta{
  1297  			testingOverrides: metaOverridesForProvider(testProvider()),
  1298  			Ui:               ui,
  1299  			View:             view,
  1300  		},
  1301  	}
  1302  
  1303  	// A missing input=false should abort rather than loop infinitely
  1304  	args = []string{"-backend-config=path=baz"}
  1305  	if code := c.Run(args); code == 0 {
  1306  		t.Fatal("init should have failed", ui.OutputWriter)
  1307  	}
  1308  }
  1309  
  1310  func TestInit_getProvider(t *testing.T) {
  1311  	// Create a temporary working directory that is empty
  1312  	td := t.TempDir()
  1313  	testCopyDir(t, testFixturePath("init-get-providers"), td)
  1314  	defer testChdir(t, td)()
  1315  
  1316  	overrides := metaOverridesForProvider(testProvider())
  1317  	ui := new(cli.MockUi)
  1318  	view, _ := testView(t)
  1319  	providerSource, close := newMockProviderSource(t, map[string][]string{
  1320  		// looking for an exact version
  1321  		"exact": {"1.2.3"},
  1322  		// config requires >= 2.3.3
  1323  		"greater-than": {"2.3.4", "2.3.3", "2.3.0"},
  1324  		// config specifies
  1325  		"between": {"3.4.5", "2.3.4", "1.2.3"},
  1326  	})
  1327  	defer close()
  1328  	m := Meta{
  1329  		testingOverrides: overrides,
  1330  		Ui:               ui,
  1331  		View:             view,
  1332  		ProviderSource:   providerSource,
  1333  	}
  1334  
  1335  	c := &InitCommand{
  1336  		Meta: m,
  1337  	}
  1338  
  1339  	args := []string{
  1340  		"-backend=false", // should be possible to install plugins without backend init
  1341  	}
  1342  	if code := c.Run(args); code != 0 {
  1343  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  1344  	}
  1345  
  1346  	// check that we got the providers for our config
  1347  	exactPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/exact/1.2.3/%s", getproviders.CurrentPlatform)
  1348  	if _, err := os.Stat(exactPath); os.IsNotExist(err) {
  1349  		t.Fatal("provider 'exact' not downloaded")
  1350  	}
  1351  	greaterThanPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/greater-than/2.3.4/%s", getproviders.CurrentPlatform)
  1352  	if _, err := os.Stat(greaterThanPath); os.IsNotExist(err) {
  1353  		t.Fatal("provider 'greater-than' not downloaded")
  1354  	}
  1355  	betweenPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/between/2.3.4/%s", getproviders.CurrentPlatform)
  1356  	if _, err := os.Stat(betweenPath); os.IsNotExist(err) {
  1357  		t.Fatal("provider 'between' not downloaded")
  1358  	}
  1359  
  1360  	t.Run("future-state", func(t *testing.T) {
  1361  		// getting providers should fail if a state from a newer version of
  1362  		// terraform exists, since InitCommand.getProviders needs to inspect that
  1363  		// state.
  1364  
  1365  		f, err := os.Create(DefaultStateFilename)
  1366  		if err != nil {
  1367  			t.Fatalf("err: %s", err)
  1368  		}
  1369  		defer f.Close()
  1370  
  1371  		// Construct a mock state file from the far future
  1372  		type FutureState struct {
  1373  			Version          uint                     `json:"version"`
  1374  			Lineage          string                   `json:"lineage"`
  1375  			TerraformVersion string                   `json:"terraform_version"`
  1376  			Outputs          map[string]interface{}   `json:"outputs"`
  1377  			Resources        []map[string]interface{} `json:"resources"`
  1378  		}
  1379  		fs := &FutureState{
  1380  			Version:          999,
  1381  			Lineage:          "123-456-789",
  1382  			TerraformVersion: "999.0.0",
  1383  			Outputs:          make(map[string]interface{}),
  1384  			Resources:        make([]map[string]interface{}, 0),
  1385  		}
  1386  		src, err := json.MarshalIndent(fs, "", "  ")
  1387  		if err != nil {
  1388  			t.Fatalf("failed to marshal future state: %s", err)
  1389  		}
  1390  		src = append(src, '\n')
  1391  		_, err = f.Write(src)
  1392  		if err != nil {
  1393  			t.Fatal(err)
  1394  		}
  1395  
  1396  		ui := new(cli.MockUi)
  1397  		view, _ := testView(t)
  1398  		m.Ui = ui
  1399  		m.View = view
  1400  		c := &InitCommand{
  1401  			Meta: m,
  1402  		}
  1403  
  1404  		if code := c.Run(nil); code == 0 {
  1405  			t.Fatal("expected error, got:", ui.OutputWriter)
  1406  		}
  1407  
  1408  		errMsg := ui.ErrorWriter.String()
  1409  		if !strings.Contains(errMsg, "Unsupported state file format") {
  1410  			t.Fatal("unexpected error:", errMsg)
  1411  		}
  1412  	})
  1413  }
  1414  
  1415  func TestInit_getProviderSource(t *testing.T) {
  1416  	// Create a temporary working directory that is empty
  1417  	td := t.TempDir()
  1418  	testCopyDir(t, testFixturePath("init-get-provider-source"), td)
  1419  	defer testChdir(t, td)()
  1420  
  1421  	overrides := metaOverridesForProvider(testProvider())
  1422  	ui := new(cli.MockUi)
  1423  	view, _ := testView(t)
  1424  	providerSource, close := newMockProviderSource(t, map[string][]string{
  1425  		// looking for an exact version
  1426  		"acme/alpha": {"1.2.3"},
  1427  		// config doesn't specify versions for other providers
  1428  		"registry.example.com/acme/beta": {"1.0.0"},
  1429  		"gamma":                          {"2.0.0"},
  1430  	})
  1431  	defer close()
  1432  	m := Meta{
  1433  		testingOverrides: overrides,
  1434  		Ui:               ui,
  1435  		View:             view,
  1436  		ProviderSource:   providerSource,
  1437  	}
  1438  
  1439  	c := &InitCommand{
  1440  		Meta: m,
  1441  	}
  1442  
  1443  	args := []string{
  1444  		"-backend=false", // should be possible to install plugins without backend init
  1445  	}
  1446  	if code := c.Run(args); code != 0 {
  1447  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  1448  	}
  1449  
  1450  	// check that we got the providers for our config
  1451  	exactPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/acme/alpha/1.2.3/%s", getproviders.CurrentPlatform)
  1452  	if _, err := os.Stat(exactPath); os.IsNotExist(err) {
  1453  		t.Error("provider 'alpha' not downloaded")
  1454  	}
  1455  	greaterThanPath := fmt.Sprintf(".terraform/providers/registry.example.com/acme/beta/1.0.0/%s", getproviders.CurrentPlatform)
  1456  	if _, err := os.Stat(greaterThanPath); os.IsNotExist(err) {
  1457  		t.Error("provider 'beta' not downloaded")
  1458  	}
  1459  	betweenPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/gamma/2.0.0/%s", getproviders.CurrentPlatform)
  1460  	if _, err := os.Stat(betweenPath); os.IsNotExist(err) {
  1461  		t.Error("provider 'gamma' not downloaded")
  1462  	}
  1463  }
  1464  
  1465  func TestInit_getProviderLegacyFromState(t *testing.T) {
  1466  	// Create a temporary working directory that is empty
  1467  	td := t.TempDir()
  1468  	testCopyDir(t, testFixturePath("init-get-provider-legacy-from-state"), td)
  1469  	defer testChdir(t, td)()
  1470  
  1471  	overrides := metaOverridesForProvider(testProvider())
  1472  	ui := new(cli.MockUi)
  1473  	view, _ := testView(t)
  1474  	providerSource, close := newMockProviderSource(t, map[string][]string{
  1475  		"acme/alpha": {"1.2.3"},
  1476  	})
  1477  	defer close()
  1478  	m := Meta{
  1479  		testingOverrides: overrides,
  1480  		Ui:               ui,
  1481  		View:             view,
  1482  		ProviderSource:   providerSource,
  1483  	}
  1484  
  1485  	c := &InitCommand{
  1486  		Meta: m,
  1487  	}
  1488  
  1489  	if code := c.Run(nil); code != 1 {
  1490  		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
  1491  	}
  1492  
  1493  	// Expect this diagnostic output
  1494  	wants := []string{
  1495  		"Invalid legacy provider address",
  1496  		"You must complete the Terraform 0.13 upgrade process",
  1497  	}
  1498  	got := ui.ErrorWriter.String()
  1499  	for _, want := range wants {
  1500  		if !strings.Contains(got, want) {
  1501  			t.Fatalf("expected output to contain %q, got:\n\n%s", want, got)
  1502  		}
  1503  	}
  1504  }
  1505  
  1506  func TestInit_getProviderInvalidPackage(t *testing.T) {
  1507  	// Create a temporary working directory that is empty
  1508  	td := t.TempDir()
  1509  	testCopyDir(t, testFixturePath("init-get-provider-invalid-package"), td)
  1510  	defer testChdir(t, td)()
  1511  
  1512  	overrides := metaOverridesForProvider(testProvider())
  1513  	ui := new(cli.MockUi)
  1514  	view, _ := testView(t)
  1515  
  1516  	// create a provider source which allows installing an invalid package
  1517  	addr := addrs.MustParseProviderSourceString("invalid/package")
  1518  	version := getproviders.MustParseVersion("1.0.0")
  1519  	meta, close, err := getproviders.FakeInstallablePackageMeta(
  1520  		addr,
  1521  		version,
  1522  		getproviders.VersionList{getproviders.MustParseVersion("5.0")},
  1523  		getproviders.CurrentPlatform,
  1524  		"terraform-package", // should be "terraform-provider-package"
  1525  	)
  1526  	defer close()
  1527  	if err != nil {
  1528  		t.Fatalf("failed to prepare fake package for %s %s: %s", addr.ForDisplay(), version, err)
  1529  	}
  1530  	providerSource := getproviders.NewMockSource([]getproviders.PackageMeta{meta}, nil)
  1531  
  1532  	m := Meta{
  1533  		testingOverrides: overrides,
  1534  		Ui:               ui,
  1535  		View:             view,
  1536  		ProviderSource:   providerSource,
  1537  	}
  1538  
  1539  	c := &InitCommand{
  1540  		Meta: m,
  1541  	}
  1542  
  1543  	args := []string{
  1544  		"-backend=false", // should be possible to install plugins without backend init
  1545  	}
  1546  	if code := c.Run(args); code != 1 {
  1547  		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
  1548  	}
  1549  
  1550  	// invalid provider should be installed
  1551  	packagePath := fmt.Sprintf(".terraform/providers/registry.terraform.io/invalid/package/1.0.0/%s/terraform-package", getproviders.CurrentPlatform)
  1552  	if _, err := os.Stat(packagePath); os.IsNotExist(err) {
  1553  		t.Fatal("provider 'invalid/package' not downloaded")
  1554  	}
  1555  
  1556  	wantErrors := []string{
  1557  		"Failed to install provider",
  1558  		"could not find executable file starting with terraform-provider-package",
  1559  	}
  1560  	got := ui.ErrorWriter.String()
  1561  	for _, wantError := range wantErrors {
  1562  		if !strings.Contains(got, wantError) {
  1563  			t.Fatalf("missing error:\nwant: %q\ngot:\n%s", wantError, got)
  1564  		}
  1565  	}
  1566  }
  1567  
  1568  func TestInit_getProviderDetectedLegacy(t *testing.T) {
  1569  	// Create a temporary working directory that is empty
  1570  	td := t.TempDir()
  1571  	testCopyDir(t, testFixturePath("init-get-provider-detected-legacy"), td)
  1572  	defer testChdir(t, td)()
  1573  
  1574  	// We need to construct a multisource with a mock source and a registry
  1575  	// source: the mock source will return ErrRegistryProviderNotKnown for an
  1576  	// unknown provider, and the registry source will allow us to look up the
  1577  	// appropriate namespace if possible.
  1578  	providerSource, psClose := newMockProviderSource(t, map[string][]string{
  1579  		"hashicorp/foo":           {"1.2.3"},
  1580  		"terraform-providers/baz": {"2.3.4"}, // this will not be installed
  1581  	})
  1582  	defer psClose()
  1583  	registrySource, rsClose := testRegistrySource(t)
  1584  	defer rsClose()
  1585  	multiSource := getproviders.MultiSource{
  1586  		{Source: providerSource},
  1587  		{Source: registrySource},
  1588  	}
  1589  
  1590  	ui := new(cli.MockUi)
  1591  	view, _ := testView(t)
  1592  	m := Meta{
  1593  		Ui:             ui,
  1594  		View:           view,
  1595  		ProviderSource: multiSource,
  1596  	}
  1597  
  1598  	c := &InitCommand{
  1599  		Meta: m,
  1600  	}
  1601  
  1602  	args := []string{
  1603  		"-backend=false", // should be possible to install plugins without backend init
  1604  	}
  1605  	if code := c.Run(args); code == 0 {
  1606  		t.Fatalf("expected error, got output: \n%s", ui.OutputWriter.String())
  1607  	}
  1608  
  1609  	// foo should be installed
  1610  	fooPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/foo/1.2.3/%s", getproviders.CurrentPlatform)
  1611  	if _, err := os.Stat(fooPath); os.IsNotExist(err) {
  1612  		t.Error("provider 'foo' not installed")
  1613  	}
  1614  	// baz should not be installed
  1615  	bazPath := fmt.Sprintf(".terraform/providers/registry.terraform.io/terraform-providers/baz/2.3.4/%s", getproviders.CurrentPlatform)
  1616  	if _, err := os.Stat(bazPath); !os.IsNotExist(err) {
  1617  		t.Error("provider 'baz' installed, but should not be")
  1618  	}
  1619  
  1620  	// error output is the main focus of this test
  1621  	errOutput := ui.ErrorWriter.String()
  1622  	errors := []string{
  1623  		"Failed to query available provider packages",
  1624  		"Could not retrieve the list of available versions",
  1625  		"registry.terraform.io/hashicorp/baz",
  1626  		"registry.terraform.io/hashicorp/frob",
  1627  	}
  1628  	for _, want := range errors {
  1629  		if !strings.Contains(errOutput, want) {
  1630  			t.Fatalf("expected error %q: %s", want, errOutput)
  1631  		}
  1632  	}
  1633  }
  1634  
  1635  func TestInit_providerSource(t *testing.T) {
  1636  	// Create a temporary working directory that is empty
  1637  	td := t.TempDir()
  1638  	testCopyDir(t, testFixturePath("init-required-providers"), td)
  1639  	defer testChdir(t, td)()
  1640  
  1641  	providerSource, close := newMockProviderSource(t, map[string][]string{
  1642  		"test":      {"1.2.3", "1.2.4"},
  1643  		"test-beta": {"1.2.4"},
  1644  		"source":    {"1.2.2", "1.2.3", "1.2.1"},
  1645  	})
  1646  	defer close()
  1647  
  1648  	ui := cli.NewMockUi()
  1649  	view, _ := testView(t)
  1650  	m := Meta{
  1651  		testingOverrides: metaOverridesForProvider(testProvider()),
  1652  		Ui:               ui,
  1653  		View:             view,
  1654  		ProviderSource:   providerSource,
  1655  	}
  1656  
  1657  	c := &InitCommand{
  1658  		Meta: m,
  1659  	}
  1660  
  1661  	args := []string{}
  1662  
  1663  	if code := c.Run(args); code != 0 {
  1664  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  1665  	}
  1666  	if strings.Contains(ui.OutputWriter.String(), "Terraform has initialized, but configuration upgrades may be needed") {
  1667  		t.Fatalf("unexpected \"configuration upgrade\" warning in output")
  1668  	}
  1669  
  1670  	cacheDir := m.providerLocalCacheDir()
  1671  	gotPackages := cacheDir.AllAvailablePackages()
  1672  	wantPackages := map[addrs.Provider][]providercache.CachedProvider{
  1673  		addrs.NewDefaultProvider("test"): {
  1674  			{
  1675  				Provider:   addrs.NewDefaultProvider("test"),
  1676  				Version:    getproviders.MustParseVersion("1.2.3"),
  1677  				PackageDir: expectedPackageInstallPath("test", "1.2.3", false),
  1678  			},
  1679  		},
  1680  		addrs.NewDefaultProvider("test-beta"): {
  1681  			{
  1682  				Provider:   addrs.NewDefaultProvider("test-beta"),
  1683  				Version:    getproviders.MustParseVersion("1.2.4"),
  1684  				PackageDir: expectedPackageInstallPath("test-beta", "1.2.4", false),
  1685  			},
  1686  		},
  1687  		addrs.NewDefaultProvider("source"): {
  1688  			{
  1689  				Provider:   addrs.NewDefaultProvider("source"),
  1690  				Version:    getproviders.MustParseVersion("1.2.3"),
  1691  				PackageDir: expectedPackageInstallPath("source", "1.2.3", false),
  1692  			},
  1693  		},
  1694  	}
  1695  	if diff := cmp.Diff(wantPackages, gotPackages); diff != "" {
  1696  		t.Errorf("wrong cache directory contents after upgrade\n%s", diff)
  1697  	}
  1698  
  1699  	locks, err := m.lockedDependencies()
  1700  	if err != nil {
  1701  		t.Fatalf("failed to get locked dependencies: %s", err)
  1702  	}
  1703  	gotProviderLocks := locks.AllProviders()
  1704  	wantProviderLocks := map[addrs.Provider]*depsfile.ProviderLock{
  1705  		addrs.NewDefaultProvider("test-beta"): depsfile.NewProviderLock(
  1706  			addrs.NewDefaultProvider("test-beta"),
  1707  			getproviders.MustParseVersion("1.2.4"),
  1708  			getproviders.MustParseVersionConstraints("= 1.2.4"),
  1709  			[]getproviders.Hash{
  1710  				getproviders.HashScheme1.New("see6W06w09Ea+AobFJ+mbvPTie6ASqZAAdlFZbs8BSM="),
  1711  			},
  1712  		),
  1713  		addrs.NewDefaultProvider("test"): depsfile.NewProviderLock(
  1714  			addrs.NewDefaultProvider("test"),
  1715  			getproviders.MustParseVersion("1.2.3"),
  1716  			getproviders.MustParseVersionConstraints("= 1.2.3"),
  1717  			[]getproviders.Hash{
  1718  				getproviders.HashScheme1.New("wlbEC2mChQZ2hhgUhl6SeVLPP7fMqOFUZAQhQ9GIIno="),
  1719  			},
  1720  		),
  1721  		addrs.NewDefaultProvider("source"): depsfile.NewProviderLock(
  1722  			addrs.NewDefaultProvider("source"),
  1723  			getproviders.MustParseVersion("1.2.3"),
  1724  			getproviders.MustParseVersionConstraints("= 1.2.3"),
  1725  			[]getproviders.Hash{
  1726  				getproviders.HashScheme1.New("myS3qb3px3tRBq1ZWRYJeUH+kySWpBc0Yy8rw6W7/p4="),
  1727  			},
  1728  		),
  1729  	}
  1730  
  1731  	if diff := cmp.Diff(gotProviderLocks, wantProviderLocks, depsfile.ProviderLockComparer); diff != "" {
  1732  		t.Errorf("wrong version selections after upgrade\n%s", diff)
  1733  	}
  1734  
  1735  	if got, want := ui.OutputWriter.String(), "Installed hashicorp/test v1.2.3 (verified checksum)"; !strings.Contains(got, want) {
  1736  		t.Fatalf("unexpected output: %s\nexpected to include %q", got, want)
  1737  	}
  1738  	if got, want := ui.ErrorWriter.String(), "\n  - hashicorp/source\n  - hashicorp/test\n  - hashicorp/test-beta"; !strings.Contains(got, want) {
  1739  		t.Fatalf("wrong error message\nshould contain: %s\ngot:\n%s", want, got)
  1740  	}
  1741  }
  1742  
  1743  func TestInit_cancelModules(t *testing.T) {
  1744  	// This test runs `terraform init` as if SIGINT (or similar on other
  1745  	// platforms) were sent to it, testing that it is interruptible.
  1746  
  1747  	td := t.TempDir()
  1748  	testCopyDir(t, testFixturePath("init-registry-module"), td)
  1749  	defer testChdir(t, td)()
  1750  
  1751  	// Our shutdown channel is pre-closed so init will exit as soon as it
  1752  	// starts a cancelable portion of the process.
  1753  	shutdownCh := make(chan struct{})
  1754  	close(shutdownCh)
  1755  
  1756  	ui := cli.NewMockUi()
  1757  	view, _ := testView(t)
  1758  	m := Meta{
  1759  		testingOverrides: metaOverridesForProvider(testProvider()),
  1760  		Ui:               ui,
  1761  		View:             view,
  1762  		ShutdownCh:       shutdownCh,
  1763  	}
  1764  
  1765  	c := &InitCommand{
  1766  		Meta: m,
  1767  	}
  1768  
  1769  	args := []string{}
  1770  
  1771  	if code := c.Run(args); code == 0 {
  1772  		t.Fatalf("succeeded; wanted error\n%s", ui.OutputWriter.String())
  1773  	}
  1774  
  1775  	if got, want := ui.ErrorWriter.String(), `Module installation was canceled by an interrupt signal`; !strings.Contains(got, want) {
  1776  		t.Fatalf("wrong error message\nshould contain: %s\ngot:\n%s", want, got)
  1777  	}
  1778  }
  1779  
  1780  func TestInit_cancelProviders(t *testing.T) {
  1781  	// This test runs `terraform init` as if SIGINT (or similar on other
  1782  	// platforms) were sent to it, testing that it is interruptible.
  1783  
  1784  	td := t.TempDir()
  1785  	testCopyDir(t, testFixturePath("init-required-providers"), td)
  1786  	defer testChdir(t, td)()
  1787  
  1788  	// Use a provider source implementation which is designed to hang indefinitely,
  1789  	// to avoid a race between the closed shutdown channel and the provider source
  1790  	// operations.
  1791  	providerSource := &getproviders.HangingSource{}
  1792  
  1793  	// Our shutdown channel is pre-closed so init will exit as soon as it
  1794  	// starts a cancelable portion of the process.
  1795  	shutdownCh := make(chan struct{})
  1796  	close(shutdownCh)
  1797  
  1798  	ui := cli.NewMockUi()
  1799  	view, _ := testView(t)
  1800  	m := Meta{
  1801  		testingOverrides: metaOverridesForProvider(testProvider()),
  1802  		Ui:               ui,
  1803  		View:             view,
  1804  		ProviderSource:   providerSource,
  1805  		ShutdownCh:       shutdownCh,
  1806  	}
  1807  
  1808  	c := &InitCommand{
  1809  		Meta: m,
  1810  	}
  1811  
  1812  	args := []string{}
  1813  
  1814  	if code := c.Run(args); code == 0 {
  1815  		t.Fatalf("succeeded; wanted error\n%s", ui.OutputWriter.String())
  1816  	}
  1817  	// Currently the first operation that is cancelable is provider
  1818  	// installation, so our error message comes from there. If we
  1819  	// make the earlier steps cancelable in future then it'd be
  1820  	// expected for this particular message to change.
  1821  	if got, want := ui.ErrorWriter.String(), `Provider installation was canceled by an interrupt signal`; !strings.Contains(got, want) {
  1822  		t.Fatalf("wrong error message\nshould contain: %s\ngot:\n%s", want, got)
  1823  	}
  1824  }
  1825  
  1826  func TestInit_getUpgradePlugins(t *testing.T) {
  1827  	// Create a temporary working directory that is empty
  1828  	td := t.TempDir()
  1829  	testCopyDir(t, testFixturePath("init-get-providers"), td)
  1830  	defer testChdir(t, td)()
  1831  
  1832  	providerSource, close := newMockProviderSource(t, map[string][]string{
  1833  		// looking for an exact version
  1834  		"exact": {"1.2.3"},
  1835  		// config requires >= 2.3.3
  1836  		"greater-than": {"2.3.4", "2.3.3", "2.3.0"},
  1837  		// config specifies > 1.0.0 , < 3.0.0
  1838  		"between": {"3.4.5", "2.3.4", "1.2.3"},
  1839  	})
  1840  	defer close()
  1841  
  1842  	ui := new(cli.MockUi)
  1843  	view, _ := testView(t)
  1844  	m := Meta{
  1845  		testingOverrides: metaOverridesForProvider(testProvider()),
  1846  		Ui:               ui,
  1847  		View:             view,
  1848  		ProviderSource:   providerSource,
  1849  	}
  1850  
  1851  	installFakeProviderPackages(t, &m, map[string][]string{
  1852  		"exact":        {"0.0.1"},
  1853  		"greater-than": {"2.3.3"},
  1854  	})
  1855  
  1856  	c := &InitCommand{
  1857  		Meta: m,
  1858  	}
  1859  
  1860  	args := []string{
  1861  		"-upgrade=true",
  1862  	}
  1863  	if code := c.Run(args); code != 0 {
  1864  		t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String())
  1865  	}
  1866  
  1867  	cacheDir := m.providerLocalCacheDir()
  1868  	gotPackages := cacheDir.AllAvailablePackages()
  1869  	wantPackages := map[addrs.Provider][]providercache.CachedProvider{
  1870  		// "between" wasn't previously installed at all, so we installed
  1871  		// the newest available version that matched the version constraints.
  1872  		addrs.NewDefaultProvider("between"): {
  1873  			{
  1874  				Provider:   addrs.NewDefaultProvider("between"),
  1875  				Version:    getproviders.MustParseVersion("2.3.4"),
  1876  				PackageDir: expectedPackageInstallPath("between", "2.3.4", false),
  1877  			},
  1878  		},
  1879  		// The existing version of "exact" did not match the version constraints,
  1880  		// so we installed what the configuration selected as well.
  1881  		addrs.NewDefaultProvider("exact"): {
  1882  			{
  1883  				Provider:   addrs.NewDefaultProvider("exact"),
  1884  				Version:    getproviders.MustParseVersion("1.2.3"),
  1885  				PackageDir: expectedPackageInstallPath("exact", "1.2.3", false),
  1886  			},
  1887  			// Previous version is still there, but not selected
  1888  			{
  1889  				Provider:   addrs.NewDefaultProvider("exact"),
  1890  				Version:    getproviders.MustParseVersion("0.0.1"),
  1891  				PackageDir: expectedPackageInstallPath("exact", "0.0.1", false),
  1892  			},
  1893  		},
  1894  		// The existing version of "greater-than" _did_ match the constraints,
  1895  		// but a newer version was available and the user specified
  1896  		// -upgrade and so we upgraded it anyway.
  1897  		addrs.NewDefaultProvider("greater-than"): {
  1898  			{
  1899  				Provider:   addrs.NewDefaultProvider("greater-than"),
  1900  				Version:    getproviders.MustParseVersion("2.3.4"),
  1901  				PackageDir: expectedPackageInstallPath("greater-than", "2.3.4", false),
  1902  			},
  1903  			// Previous version is still there, but not selected
  1904  			{
  1905  				Provider:   addrs.NewDefaultProvider("greater-than"),
  1906  				Version:    getproviders.MustParseVersion("2.3.3"),
  1907  				PackageDir: expectedPackageInstallPath("greater-than", "2.3.3", false),
  1908  			},
  1909  		},
  1910  	}
  1911  	if diff := cmp.Diff(wantPackages, gotPackages); diff != "" {
  1912  		t.Errorf("wrong cache directory contents after upgrade\n%s", diff)
  1913  	}
  1914  
  1915  	locks, err := m.lockedDependencies()
  1916  	if err != nil {
  1917  		t.Fatalf("failed to get locked dependencies: %s", err)
  1918  	}
  1919  	gotProviderLocks := locks.AllProviders()
  1920  	wantProviderLocks := map[addrs.Provider]*depsfile.ProviderLock{
  1921  		addrs.NewDefaultProvider("between"): depsfile.NewProviderLock(
  1922  			addrs.NewDefaultProvider("between"),
  1923  			getproviders.MustParseVersion("2.3.4"),
  1924  			getproviders.MustParseVersionConstraints("> 1.0.0, < 3.0.0"),
  1925  			[]getproviders.Hash{
  1926  				getproviders.HashScheme1.New("JVqAvZz88A+hS2wHVtTWQkHaxoA/LrUAz0H3jPBWPIA="),
  1927  			},
  1928  		),
  1929  		addrs.NewDefaultProvider("exact"): depsfile.NewProviderLock(
  1930  			addrs.NewDefaultProvider("exact"),
  1931  			getproviders.MustParseVersion("1.2.3"),
  1932  			getproviders.MustParseVersionConstraints("= 1.2.3"),
  1933  			[]getproviders.Hash{
  1934  				getproviders.HashScheme1.New("H1TxWF8LyhBb6B4iUdKhLc/S9sC/jdcrCykpkbGcfbg="),
  1935  			},
  1936  		),
  1937  		addrs.NewDefaultProvider("greater-than"): depsfile.NewProviderLock(
  1938  			addrs.NewDefaultProvider("greater-than"),
  1939  			getproviders.MustParseVersion("2.3.4"),
  1940  			getproviders.MustParseVersionConstraints(">= 2.3.3"),
  1941  			[]getproviders.Hash{
  1942  				getproviders.HashScheme1.New("SJPpXx/yoFE/W+7eCipjJ+G21xbdnTBD7lWodZ8hWkU="),
  1943  			},
  1944  		),
  1945  	}
  1946  	if diff := cmp.Diff(gotProviderLocks, wantProviderLocks, depsfile.ProviderLockComparer); diff != "" {
  1947  		t.Errorf("wrong version selections after upgrade\n%s", diff)
  1948  	}
  1949  }
  1950  
  1951  func TestInit_getProviderMissing(t *testing.T) {
  1952  	// Create a temporary working directory that is empty
  1953  	td := t.TempDir()
  1954  	testCopyDir(t, testFixturePath("init-get-providers"), td)
  1955  	defer testChdir(t, td)()
  1956  
  1957  	providerSource, close := newMockProviderSource(t, map[string][]string{
  1958  		// looking for exact version 1.2.3
  1959  		"exact": {"1.2.4"},
  1960  		// config requires >= 2.3.3
  1961  		"greater-than": {"2.3.4", "2.3.3", "2.3.0"},
  1962  		// config specifies
  1963  		"between": {"3.4.5", "2.3.4", "1.2.3"},
  1964  	})
  1965  	defer close()
  1966  
  1967  	ui := new(cli.MockUi)
  1968  	view, _ := testView(t)
  1969  	m := Meta{
  1970  		testingOverrides: metaOverridesForProvider(testProvider()),
  1971  		Ui:               ui,
  1972  		View:             view,
  1973  		ProviderSource:   providerSource,
  1974  	}
  1975  
  1976  	c := &InitCommand{
  1977  		Meta: m,
  1978  	}
  1979  
  1980  	args := []string{}
  1981  	if code := c.Run(args); code == 0 {
  1982  		t.Fatalf("expected error, got output: \n%s", ui.OutputWriter.String())
  1983  	}
  1984  
  1985  	if !strings.Contains(ui.ErrorWriter.String(), "no available releases match") {
  1986  		t.Fatalf("unexpected error output: %s", ui.ErrorWriter)
  1987  	}
  1988  }
  1989  
  1990  func TestInit_checkRequiredVersion(t *testing.T) {
  1991  	// Create a temporary working directory that is empty
  1992  	td := t.TempDir()
  1993  	testCopyDir(t, testFixturePath("init-check-required-version"), td)
  1994  	defer testChdir(t, td)()
  1995  
  1996  	ui := cli.NewMockUi()
  1997  	view, _ := testView(t)
  1998  	c := &InitCommand{
  1999  		Meta: Meta{
  2000  			testingOverrides: metaOverridesForProvider(testProvider()),
  2001  			Ui:               ui,
  2002  			View:             view,
  2003  		},
  2004  	}
  2005  
  2006  	args := []string{}
  2007  	if code := c.Run(args); code != 1 {
  2008  		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
  2009  	}
  2010  	errStr := ui.ErrorWriter.String()
  2011  	if !strings.Contains(errStr, `required_version = "~> 0.9.0"`) {
  2012  		t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
  2013  	}
  2014  	if strings.Contains(errStr, `required_version = ">= 0.13.0"`) {
  2015  		t.Fatalf("output should not point to met version constraint, but is:\n\n%s", errStr)
  2016  	}
  2017  }
  2018  
  2019  // Verify that init will error out with an invalid version constraint, even if
  2020  // there are other invalid configuration constructs.
  2021  func TestInit_checkRequiredVersionFirst(t *testing.T) {
  2022  	t.Run("root_module", func(t *testing.T) {
  2023  		td := t.TempDir()
  2024  		testCopyDir(t, testFixturePath("init-check-required-version-first"), td)
  2025  		defer testChdir(t, td)()
  2026  
  2027  		ui := cli.NewMockUi()
  2028  		view, _ := testView(t)
  2029  		c := &InitCommand{
  2030  			Meta: Meta{
  2031  				testingOverrides: metaOverridesForProvider(testProvider()),
  2032  				Ui:               ui,
  2033  				View:             view,
  2034  			},
  2035  		}
  2036  
  2037  		args := []string{}
  2038  		if code := c.Run(args); code != 1 {
  2039  			t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
  2040  		}
  2041  		errStr := ui.ErrorWriter.String()
  2042  		if !strings.Contains(errStr, `Unsupported Terraform Core version`) {
  2043  			t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
  2044  		}
  2045  	})
  2046  	t.Run("sub_module", func(t *testing.T) {
  2047  		td := t.TempDir()
  2048  		testCopyDir(t, testFixturePath("init-check-required-version-first-module"), td)
  2049  		defer testChdir(t, td)()
  2050  
  2051  		ui := cli.NewMockUi()
  2052  		view, _ := testView(t)
  2053  		c := &InitCommand{
  2054  			Meta: Meta{
  2055  				testingOverrides: metaOverridesForProvider(testProvider()),
  2056  				Ui:               ui,
  2057  				View:             view,
  2058  			},
  2059  		}
  2060  
  2061  		args := []string{}
  2062  		if code := c.Run(args); code != 1 {
  2063  			t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
  2064  		}
  2065  		errStr := ui.ErrorWriter.String()
  2066  		if !strings.Contains(errStr, `Unsupported Terraform Core version`) {
  2067  			t.Fatalf("output should point to unmet version constraint, but is:\n\n%s", errStr)
  2068  		}
  2069  	})
  2070  }
  2071  
  2072  func TestInit_providerLockFile(t *testing.T) {
  2073  	// Create a temporary working directory that is empty
  2074  	td := t.TempDir()
  2075  	testCopyDir(t, testFixturePath("init-provider-lock-file"), td)
  2076  	// The temporary directory does not have write permission (dr-xr-xr-x) after the copy
  2077  	defer os.Chmod(td, os.ModePerm)
  2078  	defer testChdir(t, td)()
  2079  
  2080  	providerSource, close := newMockProviderSource(t, map[string][]string{
  2081  		"test": {"1.2.3"},
  2082  	})
  2083  	defer close()
  2084  
  2085  	ui := new(cli.MockUi)
  2086  	view, _ := testView(t)
  2087  	m := Meta{
  2088  		testingOverrides: metaOverridesForProvider(testProvider()),
  2089  		Ui:               ui,
  2090  		View:             view,
  2091  		ProviderSource:   providerSource,
  2092  	}
  2093  
  2094  	c := &InitCommand{
  2095  		Meta: m,
  2096  	}
  2097  
  2098  	args := []string{}
  2099  	if code := c.Run(args); code != 0 {
  2100  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  2101  	}
  2102  
  2103  	lockFile := ".terraform.lock.hcl"
  2104  	buf, err := ioutil.ReadFile(lockFile)
  2105  	if err != nil {
  2106  		t.Fatalf("failed to read dependency lock file %s: %s", lockFile, err)
  2107  	}
  2108  	buf = bytes.TrimSpace(buf)
  2109  	// The hash in here is for the fake package that newMockProviderSource produces
  2110  	// (so it'll change if newMockProviderSource starts producing different contents)
  2111  	wantLockFile := strings.TrimSpace(`
  2112  # This file is maintained automatically by "terraform init".
  2113  # Manual edits may be lost in future updates.
  2114  
  2115  provider "registry.terraform.io/hashicorp/test" {
  2116    version     = "1.2.3"
  2117    constraints = "1.2.3"
  2118    hashes = [
  2119      "h1:wlbEC2mChQZ2hhgUhl6SeVLPP7fMqOFUZAQhQ9GIIno=",
  2120    ]
  2121  }
  2122  `)
  2123  	if diff := cmp.Diff(wantLockFile, string(buf)); diff != "" {
  2124  		t.Errorf("wrong dependency lock file contents\n%s", diff)
  2125  	}
  2126  
  2127  	// Make the local directory read-only, and verify that rerunning init
  2128  	// succeeds, to ensure that we don't try to rewrite an unchanged lock file
  2129  	os.Chmod(".", 0555)
  2130  	if code := c.Run(args); code != 0 {
  2131  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  2132  	}
  2133  }
  2134  
  2135  func TestInit_providerLockFileReadonly(t *testing.T) {
  2136  	// The hash in here is for the fake package that newMockProviderSource produces
  2137  	// (so it'll change if newMockProviderSource starts producing different contents)
  2138  	inputLockFile := strings.TrimSpace(`
  2139  # This file is maintained automatically by "terraform init".
  2140  # Manual edits may be lost in future updates.
  2141  
  2142  provider "registry.terraform.io/hashicorp/test" {
  2143    version     = "1.2.3"
  2144    constraints = "1.2.3"
  2145    hashes = [
  2146      "zh:e919b507a91e23a00da5c2c4d0b64bcc7900b68d43b3951ac0f6e5d80387fbdc",
  2147    ]
  2148  }
  2149  `)
  2150  
  2151  	badLockFile := strings.TrimSpace(`
  2152  # This file is maintained automatically by "terraform init".
  2153  # Manual edits may be lost in future updates.
  2154  
  2155  provider "registry.terraform.io/hashicorp/test" {
  2156    version     = "1.2.3"
  2157    constraints = "1.2.3"
  2158    hashes = [
  2159      "zh:0000000000000000000000000000000000000000000000000000000000000000",
  2160    ]
  2161  }
  2162  `)
  2163  
  2164  	updatedLockFile := strings.TrimSpace(`
  2165  # This file is maintained automatically by "terraform init".
  2166  # Manual edits may be lost in future updates.
  2167  
  2168  provider "registry.terraform.io/hashicorp/test" {
  2169    version     = "1.2.3"
  2170    constraints = "1.2.3"
  2171    hashes = [
  2172      "h1:wlbEC2mChQZ2hhgUhl6SeVLPP7fMqOFUZAQhQ9GIIno=",
  2173      "zh:e919b507a91e23a00da5c2c4d0b64bcc7900b68d43b3951ac0f6e5d80387fbdc",
  2174    ]
  2175  }
  2176  `)
  2177  
  2178  	emptyUpdatedLockFile := strings.TrimSpace(`
  2179  # This file is maintained automatically by "terraform init".
  2180  # Manual edits may be lost in future updates.
  2181  `)
  2182  
  2183  	cases := []struct {
  2184  		desc      string
  2185  		fixture   string
  2186  		providers map[string][]string
  2187  		input     string
  2188  		args      []string
  2189  		ok        bool
  2190  		want      string
  2191  	}{
  2192  		{
  2193  			desc:      "default",
  2194  			fixture:   "init-provider-lock-file",
  2195  			providers: map[string][]string{"test": {"1.2.3"}},
  2196  			input:     inputLockFile,
  2197  			args:      []string{},
  2198  			ok:        true,
  2199  			want:      updatedLockFile,
  2200  		},
  2201  		{
  2202  			desc:      "unused provider",
  2203  			fixture:   "init-provider-now-unused",
  2204  			providers: map[string][]string{"test": {"1.2.3"}},
  2205  			input:     inputLockFile,
  2206  			args:      []string{},
  2207  			ok:        true,
  2208  			want:      emptyUpdatedLockFile,
  2209  		},
  2210  		{
  2211  			desc:      "readonly",
  2212  			fixture:   "init-provider-lock-file",
  2213  			providers: map[string][]string{"test": {"1.2.3"}},
  2214  			input:     inputLockFile,
  2215  			args:      []string{"-lockfile=readonly"},
  2216  			ok:        true,
  2217  			want:      inputLockFile,
  2218  		},
  2219  		{
  2220  			desc:      "unused provider readonly",
  2221  			fixture:   "init-provider-now-unused",
  2222  			providers: map[string][]string{"test": {"1.2.3"}},
  2223  			input:     inputLockFile,
  2224  			args:      []string{"-lockfile=readonly"},
  2225  			ok:        false,
  2226  			want:      inputLockFile,
  2227  		},
  2228  		{
  2229  			desc:      "conflict",
  2230  			fixture:   "init-provider-lock-file",
  2231  			providers: map[string][]string{"test": {"1.2.3"}},
  2232  			input:     inputLockFile,
  2233  			args:      []string{"-lockfile=readonly", "-upgrade"},
  2234  			ok:        false,
  2235  			want:      inputLockFile,
  2236  		},
  2237  		{
  2238  			desc:      "checksum mismatch",
  2239  			fixture:   "init-provider-lock-file",
  2240  			providers: map[string][]string{"test": {"1.2.3"}},
  2241  			input:     badLockFile,
  2242  			args:      []string{"-lockfile=readonly"},
  2243  			ok:        false,
  2244  			want:      badLockFile,
  2245  		},
  2246  		{
  2247  			desc:    "reject to change required provider dependences",
  2248  			fixture: "init-provider-lock-file-readonly-add",
  2249  			providers: map[string][]string{
  2250  				"test": {"1.2.3"},
  2251  				"foo":  {"1.0.0"},
  2252  			},
  2253  			input: inputLockFile,
  2254  			args:  []string{"-lockfile=readonly"},
  2255  			ok:    false,
  2256  			want:  inputLockFile,
  2257  		},
  2258  	}
  2259  
  2260  	for _, tc := range cases {
  2261  		t.Run(tc.desc, func(t *testing.T) {
  2262  			// Create a temporary working directory that is empty
  2263  			td := t.TempDir()
  2264  			testCopyDir(t, testFixturePath(tc.fixture), td)
  2265  			defer testChdir(t, td)()
  2266  
  2267  			providerSource, close := newMockProviderSource(t, tc.providers)
  2268  			defer close()
  2269  
  2270  			ui := new(cli.MockUi)
  2271  			m := Meta{
  2272  				testingOverrides: metaOverridesForProvider(testProvider()),
  2273  				Ui:               ui,
  2274  				ProviderSource:   providerSource,
  2275  			}
  2276  
  2277  			c := &InitCommand{
  2278  				Meta: m,
  2279  			}
  2280  
  2281  			// write input lockfile
  2282  			lockFile := ".terraform.lock.hcl"
  2283  			if err := ioutil.WriteFile(lockFile, []byte(tc.input), 0644); err != nil {
  2284  				t.Fatalf("failed to write input lockfile: %s", err)
  2285  			}
  2286  
  2287  			code := c.Run(tc.args)
  2288  			if tc.ok && code != 0 {
  2289  				t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  2290  			}
  2291  			if !tc.ok && code == 0 {
  2292  				t.Fatalf("expected error, got output: \n%s", ui.OutputWriter.String())
  2293  			}
  2294  
  2295  			buf, err := ioutil.ReadFile(lockFile)
  2296  			if err != nil {
  2297  				t.Fatalf("failed to read dependency lock file %s: %s", lockFile, err)
  2298  			}
  2299  			buf = bytes.TrimSpace(buf)
  2300  			if diff := cmp.Diff(tc.want, string(buf)); diff != "" {
  2301  				t.Errorf("wrong dependency lock file contents\n%s", diff)
  2302  			}
  2303  		})
  2304  	}
  2305  }
  2306  
  2307  func TestInit_pluginDirReset(t *testing.T) {
  2308  	td := testTempDir(t)
  2309  	defer os.RemoveAll(td)
  2310  	defer testChdir(t, td)()
  2311  
  2312  	// An empty provider source
  2313  	providerSource, close := newMockProviderSource(t, nil)
  2314  	defer close()
  2315  
  2316  	ui := new(cli.MockUi)
  2317  	view, _ := testView(t)
  2318  	c := &InitCommand{
  2319  		Meta: Meta{
  2320  			testingOverrides: metaOverridesForProvider(testProvider()),
  2321  			Ui:               ui,
  2322  			View:             view,
  2323  			ProviderSource:   providerSource,
  2324  		},
  2325  	}
  2326  
  2327  	// make our vendor paths
  2328  	pluginPath := []string{"a", "b", "c"}
  2329  	for _, p := range pluginPath {
  2330  		if err := os.MkdirAll(p, 0755); err != nil {
  2331  			t.Fatal(err)
  2332  		}
  2333  	}
  2334  
  2335  	// run once and save the -plugin-dir
  2336  	args := []string{"-plugin-dir", "a"}
  2337  	if code := c.Run(args); code != 0 {
  2338  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  2339  	}
  2340  
  2341  	pluginDirs, err := c.loadPluginPath()
  2342  	if err != nil {
  2343  		t.Fatal(err)
  2344  	}
  2345  
  2346  	if len(pluginDirs) != 1 || pluginDirs[0] != "a" {
  2347  		t.Fatalf(`expected plugin dir ["a"], got %q`, pluginDirs)
  2348  	}
  2349  
  2350  	ui = new(cli.MockUi)
  2351  	c = &InitCommand{
  2352  		Meta: Meta{
  2353  			testingOverrides: metaOverridesForProvider(testProvider()),
  2354  			Ui:               ui,
  2355  			View:             view,
  2356  			ProviderSource:   providerSource, // still empty
  2357  		},
  2358  	}
  2359  
  2360  	// make sure we remove the plugin-dir record
  2361  	args = []string{"-plugin-dir="}
  2362  	if code := c.Run(args); code != 0 {
  2363  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  2364  	}
  2365  
  2366  	pluginDirs, err = c.loadPluginPath()
  2367  	if err != nil {
  2368  		t.Fatal(err)
  2369  	}
  2370  
  2371  	if len(pluginDirs) != 0 {
  2372  		t.Fatalf("expected no plugin dirs got %q", pluginDirs)
  2373  	}
  2374  }
  2375  
  2376  // Test user-supplied -plugin-dir
  2377  func TestInit_pluginDirProviders(t *testing.T) {
  2378  	td := t.TempDir()
  2379  	testCopyDir(t, testFixturePath("init-get-providers"), td)
  2380  	defer testChdir(t, td)()
  2381  
  2382  	// An empty provider source
  2383  	providerSource, close := newMockProviderSource(t, nil)
  2384  	defer close()
  2385  
  2386  	ui := new(cli.MockUi)
  2387  	view, _ := testView(t)
  2388  	m := Meta{
  2389  		testingOverrides: metaOverridesForProvider(testProvider()),
  2390  		Ui:               ui,
  2391  		View:             view,
  2392  		ProviderSource:   providerSource,
  2393  	}
  2394  
  2395  	c := &InitCommand{
  2396  		Meta: m,
  2397  	}
  2398  
  2399  	// make our vendor paths
  2400  	pluginPath := []string{"a", "b", "c"}
  2401  	for _, p := range pluginPath {
  2402  		if err := os.MkdirAll(p, 0755); err != nil {
  2403  			t.Fatal(err)
  2404  		}
  2405  	}
  2406  
  2407  	// We'll put some providers in our plugin dirs. To do this, we'll pretend
  2408  	// for a moment that they are provider cache directories just because that
  2409  	// allows us to lean on our existing test helper functions to do this.
  2410  	for i, def := range [][]string{
  2411  		{"exact", "1.2.3"},
  2412  		{"greater-than", "2.3.4"},
  2413  		{"between", "2.3.4"},
  2414  	} {
  2415  		name, version := def[0], def[1]
  2416  		dir := providercache.NewDir(pluginPath[i])
  2417  		installFakeProviderPackagesElsewhere(t, dir, map[string][]string{
  2418  			name: {version},
  2419  		})
  2420  	}
  2421  
  2422  	args := []string{
  2423  		"-plugin-dir", "a",
  2424  		"-plugin-dir", "b",
  2425  		"-plugin-dir", "c",
  2426  	}
  2427  	if code := c.Run(args); code != 0 {
  2428  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  2429  	}
  2430  
  2431  	locks, err := m.lockedDependencies()
  2432  	if err != nil {
  2433  		t.Fatalf("failed to get locked dependencies: %s", err)
  2434  	}
  2435  	gotProviderLocks := locks.AllProviders()
  2436  	wantProviderLocks := map[addrs.Provider]*depsfile.ProviderLock{
  2437  		addrs.NewDefaultProvider("between"): depsfile.NewProviderLock(
  2438  			addrs.NewDefaultProvider("between"),
  2439  			getproviders.MustParseVersion("2.3.4"),
  2440  			getproviders.MustParseVersionConstraints("> 1.0.0, < 3.0.0"),
  2441  			[]getproviders.Hash{
  2442  				getproviders.HashScheme1.New("JVqAvZz88A+hS2wHVtTWQkHaxoA/LrUAz0H3jPBWPIA="),
  2443  			},
  2444  		),
  2445  		addrs.NewDefaultProvider("exact"): depsfile.NewProviderLock(
  2446  			addrs.NewDefaultProvider("exact"),
  2447  			getproviders.MustParseVersion("1.2.3"),
  2448  			getproviders.MustParseVersionConstraints("= 1.2.3"),
  2449  			[]getproviders.Hash{
  2450  				getproviders.HashScheme1.New("H1TxWF8LyhBb6B4iUdKhLc/S9sC/jdcrCykpkbGcfbg="),
  2451  			},
  2452  		),
  2453  		addrs.NewDefaultProvider("greater-than"): depsfile.NewProviderLock(
  2454  			addrs.NewDefaultProvider("greater-than"),
  2455  			getproviders.MustParseVersion("2.3.4"),
  2456  			getproviders.MustParseVersionConstraints(">= 2.3.3"),
  2457  			[]getproviders.Hash{
  2458  				getproviders.HashScheme1.New("SJPpXx/yoFE/W+7eCipjJ+G21xbdnTBD7lWodZ8hWkU="),
  2459  			},
  2460  		),
  2461  	}
  2462  	if diff := cmp.Diff(gotProviderLocks, wantProviderLocks, depsfile.ProviderLockComparer); diff != "" {
  2463  		t.Errorf("wrong version selections after upgrade\n%s", diff)
  2464  	}
  2465  
  2466  	// -plugin-dir overrides the normal provider source, so it should not have
  2467  	// seen any calls at all.
  2468  	if calls := providerSource.CallLog(); len(calls) > 0 {
  2469  		t.Errorf("unexpected provider source calls (want none)\n%s", spew.Sdump(calls))
  2470  	}
  2471  }
  2472  
  2473  // Test user-supplied -plugin-dir doesn't allow auto-install
  2474  func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
  2475  	td := t.TempDir()
  2476  	testCopyDir(t, testFixturePath("init-get-providers"), td)
  2477  	defer testChdir(t, td)()
  2478  
  2479  	// Our provider source has a suitable package for "between" available,
  2480  	// but we should ignore it because -plugin-dir is set and thus this
  2481  	// source is temporarily overridden during install.
  2482  	providerSource, close := newMockProviderSource(t, map[string][]string{
  2483  		"between": {"2.3.4"},
  2484  	})
  2485  	defer close()
  2486  
  2487  	ui := cli.NewMockUi()
  2488  	view, _ := testView(t)
  2489  	m := Meta{
  2490  		testingOverrides: metaOverridesForProvider(testProvider()),
  2491  		Ui:               ui,
  2492  		View:             view,
  2493  		ProviderSource:   providerSource,
  2494  	}
  2495  
  2496  	c := &InitCommand{
  2497  		Meta: m,
  2498  	}
  2499  
  2500  	// make our vendor paths
  2501  	pluginPath := []string{"a", "b"}
  2502  	for _, p := range pluginPath {
  2503  		if err := os.MkdirAll(p, 0755); err != nil {
  2504  			t.Fatal(err)
  2505  		}
  2506  	}
  2507  
  2508  	// We'll put some providers in our plugin dirs. To do this, we'll pretend
  2509  	// for a moment that they are provider cache directories just because that
  2510  	// allows us to lean on our existing test helper functions to do this.
  2511  	for i, def := range [][]string{
  2512  		{"exact", "1.2.3"},
  2513  		{"greater-than", "2.3.4"},
  2514  	} {
  2515  		name, version := def[0], def[1]
  2516  		dir := providercache.NewDir(pluginPath[i])
  2517  		installFakeProviderPackagesElsewhere(t, dir, map[string][]string{
  2518  			name: {version},
  2519  		})
  2520  	}
  2521  
  2522  	args := []string{
  2523  		"-plugin-dir", "a",
  2524  		"-plugin-dir", "b",
  2525  	}
  2526  	if code := c.Run(args); code == 0 {
  2527  		// should have been an error
  2528  		t.Fatalf("succeeded; want error\nstdout:\n%s\nstderr\n%s", ui.OutputWriter, ui.ErrorWriter)
  2529  	}
  2530  
  2531  	// The error output should mention the "between" provider but should not
  2532  	// mention either the "exact" or "greater-than" provider, because the
  2533  	// latter two are available via the -plugin-dir directories.
  2534  	errStr := ui.ErrorWriter.String()
  2535  	if subStr := "hashicorp/between"; !strings.Contains(errStr, subStr) {
  2536  		t.Errorf("error output should mention the 'between' provider\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2537  	}
  2538  	if subStr := "hashicorp/exact"; strings.Contains(errStr, subStr) {
  2539  		t.Errorf("error output should not mention the 'exact' provider\ndo not want substr: %s\ngot:\n%s", subStr, errStr)
  2540  	}
  2541  	if subStr := "hashicorp/greater-than"; strings.Contains(errStr, subStr) {
  2542  		t.Errorf("error output should not mention the 'greater-than' provider\ndo not want substr: %s\ngot:\n%s", subStr, errStr)
  2543  	}
  2544  
  2545  	if calls := providerSource.CallLog(); len(calls) > 0 {
  2546  		t.Errorf("unexpected provider source calls (want none)\n%s", spew.Sdump(calls))
  2547  	}
  2548  }
  2549  
  2550  // Verify that plugin-dir doesn't prevent discovery of internal providers
  2551  func TestInit_pluginDirWithBuiltIn(t *testing.T) {
  2552  	td := t.TempDir()
  2553  	testCopyDir(t, testFixturePath("init-internal"), td)
  2554  	defer testChdir(t, td)()
  2555  
  2556  	// An empty provider source
  2557  	providerSource, close := newMockProviderSource(t, nil)
  2558  	defer close()
  2559  
  2560  	ui := cli.NewMockUi()
  2561  	view, _ := testView(t)
  2562  	m := Meta{
  2563  		testingOverrides: metaOverridesForProvider(testProvider()),
  2564  		Ui:               ui,
  2565  		View:             view,
  2566  		ProviderSource:   providerSource,
  2567  	}
  2568  
  2569  	c := &InitCommand{
  2570  		Meta: m,
  2571  	}
  2572  
  2573  	args := []string{"-plugin-dir", "./"}
  2574  	if code := c.Run(args); code != 0 {
  2575  		t.Fatalf("error: %s", ui.ErrorWriter)
  2576  	}
  2577  
  2578  	outputStr := ui.OutputWriter.String()
  2579  	if subStr := "terraform.io/builtin/terraform is built in to Terraform"; !strings.Contains(outputStr, subStr) {
  2580  		t.Errorf("output should mention the terraform provider\nwant substr: %s\ngot:\n%s", subStr, outputStr)
  2581  	}
  2582  }
  2583  
  2584  func TestInit_invalidBuiltInProviders(t *testing.T) {
  2585  	// This test fixture includes two invalid provider dependencies:
  2586  	// - an implied dependency on terraform.io/builtin/terraform with an
  2587  	//   explicit version number, which is not allowed because it's builtin.
  2588  	// - an explicit dependency on terraform.io/builtin/nonexist, which does
  2589  	//   not exist at all.
  2590  	td := t.TempDir()
  2591  	testCopyDir(t, testFixturePath("init-internal-invalid"), td)
  2592  	defer testChdir(t, td)()
  2593  
  2594  	// An empty provider source
  2595  	providerSource, close := newMockProviderSource(t, nil)
  2596  	defer close()
  2597  
  2598  	ui := cli.NewMockUi()
  2599  	view, _ := testView(t)
  2600  	m := Meta{
  2601  		testingOverrides: metaOverridesForProvider(testProvider()),
  2602  		Ui:               ui,
  2603  		View:             view,
  2604  		ProviderSource:   providerSource,
  2605  	}
  2606  
  2607  	c := &InitCommand{
  2608  		Meta: m,
  2609  	}
  2610  
  2611  	if code := c.Run(nil); code == 0 {
  2612  		t.Fatalf("succeeded, but was expecting error\nstdout:\n%s\nstderr:\n%s", ui.OutputWriter, ui.ErrorWriter)
  2613  	}
  2614  
  2615  	errStr := ui.ErrorWriter.String()
  2616  	if subStr := "Cannot use terraform.io/builtin/terraform: built-in"; !strings.Contains(errStr, subStr) {
  2617  		t.Errorf("error output should mention the terraform provider\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2618  	}
  2619  	if subStr := "Cannot use terraform.io/builtin/nonexist: this Terraform release"; !strings.Contains(errStr, subStr) {
  2620  		t.Errorf("error output should mention the 'nonexist' provider\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2621  	}
  2622  }
  2623  
  2624  func TestInit_invalidSyntaxNoBackend(t *testing.T) {
  2625  	td := t.TempDir()
  2626  	testCopyDir(t, testFixturePath("init-syntax-invalid-no-backend"), td)
  2627  	defer testChdir(t, td)()
  2628  
  2629  	ui := cli.NewMockUi()
  2630  	view, _ := testView(t)
  2631  	m := Meta{
  2632  		Ui:   ui,
  2633  		View: view,
  2634  	}
  2635  
  2636  	c := &InitCommand{
  2637  		Meta: m,
  2638  	}
  2639  
  2640  	if code := c.Run(nil); code == 0 {
  2641  		t.Fatalf("succeeded, but was expecting error\nstdout:\n%s\nstderr:\n%s", ui.OutputWriter, ui.ErrorWriter)
  2642  	}
  2643  
  2644  	errStr := ui.ErrorWriter.String()
  2645  	if subStr := "There are some problems with the configuration, described below"; !strings.Contains(errStr, subStr) {
  2646  		t.Errorf("Error output should include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2647  	}
  2648  	if subStr := "Error: Unsupported block type"; !strings.Contains(errStr, subStr) {
  2649  		t.Errorf("Error output should mention the syntax problem\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2650  	}
  2651  }
  2652  
  2653  func TestInit_invalidSyntaxWithBackend(t *testing.T) {
  2654  	td := t.TempDir()
  2655  	testCopyDir(t, testFixturePath("init-syntax-invalid-with-backend"), td)
  2656  	defer testChdir(t, td)()
  2657  
  2658  	ui := cli.NewMockUi()
  2659  	view, _ := testView(t)
  2660  	m := Meta{
  2661  		Ui:   ui,
  2662  		View: view,
  2663  	}
  2664  
  2665  	c := &InitCommand{
  2666  		Meta: m,
  2667  	}
  2668  
  2669  	if code := c.Run(nil); code == 0 {
  2670  		t.Fatalf("succeeded, but was expecting error\nstdout:\n%s\nstderr:\n%s", ui.OutputWriter, ui.ErrorWriter)
  2671  	}
  2672  
  2673  	errStr := ui.ErrorWriter.String()
  2674  	if subStr := "There are some problems with the configuration, described below"; !strings.Contains(errStr, subStr) {
  2675  		t.Errorf("Error output should include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2676  	}
  2677  	if subStr := "Error: Unsupported block type"; !strings.Contains(errStr, subStr) {
  2678  		t.Errorf("Error output should mention the syntax problem\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2679  	}
  2680  }
  2681  
  2682  func TestInit_invalidSyntaxInvalidBackend(t *testing.T) {
  2683  	td := t.TempDir()
  2684  	testCopyDir(t, testFixturePath("init-syntax-invalid-backend-invalid"), td)
  2685  	defer testChdir(t, td)()
  2686  
  2687  	ui := cli.NewMockUi()
  2688  	view, _ := testView(t)
  2689  	m := Meta{
  2690  		Ui:   ui,
  2691  		View: view,
  2692  	}
  2693  
  2694  	c := &InitCommand{
  2695  		Meta: m,
  2696  	}
  2697  
  2698  	if code := c.Run(nil); code == 0 {
  2699  		t.Fatalf("succeeded, but was expecting error\nstdout:\n%s\nstderr:\n%s", ui.OutputWriter, ui.ErrorWriter)
  2700  	}
  2701  
  2702  	errStr := ui.ErrorWriter.String()
  2703  	if subStr := "There are some problems with the configuration, described below"; strings.Contains(errStr, subStr) {
  2704  		t.Errorf("Error output should not include preamble\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2705  	}
  2706  	if subStr := "Error: Unsupported block type"; strings.Contains(errStr, subStr) {
  2707  		t.Errorf("Error output should not mention syntax errors\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2708  	}
  2709  	if subStr := "Error: Unsupported backend type"; !strings.Contains(errStr, subStr) {
  2710  		t.Errorf("Error output should mention the invalid backend\nwant substr: %s\ngot:\n%s", subStr, errStr)
  2711  	}
  2712  }
  2713  
  2714  // newMockProviderSource is a helper to succinctly construct a mock provider
  2715  // source that contains a set of packages matching the given provider versions
  2716  // that are available for installation (from temporary local files).
  2717  //
  2718  // The caller must call the returned close callback once the source is no
  2719  // longer needed, at which point it will clean up all of the temporary files
  2720  // and the packages in the source will no longer be available for installation.
  2721  //
  2722  // Provider addresses must be valid source strings, and passing only the
  2723  // provider name will be interpreted as a "default" provider under
  2724  // registry.terraform.io/hashicorp. If you need more control over the
  2725  // provider addresses, pass a full provider source string.
  2726  //
  2727  // This function also registers providers as belonging to the current platform,
  2728  // to ensure that they will be available to a provider installer operating in
  2729  // its default configuration.
  2730  //
  2731  // In case of any errors while constructing the source, this function will
  2732  // abort the current test using the given testing.T. Therefore a caller can
  2733  // assume that if this function returns then the result is valid and ready
  2734  // to use.
  2735  func newMockProviderSource(t *testing.T, availableProviderVersions map[string][]string) (source *getproviders.MockSource, close func()) {
  2736  	t.Helper()
  2737  	var packages []getproviders.PackageMeta
  2738  	var closes []func()
  2739  	close = func() {
  2740  		for _, f := range closes {
  2741  			f()
  2742  		}
  2743  	}
  2744  	for source, versions := range availableProviderVersions {
  2745  		addr := addrs.MustParseProviderSourceString(source)
  2746  		for _, versionStr := range versions {
  2747  			version, err := getproviders.ParseVersion(versionStr)
  2748  			if err != nil {
  2749  				close()
  2750  				t.Fatalf("failed to parse %q as a version number for %q: %s", versionStr, addr.ForDisplay(), err)
  2751  			}
  2752  			meta, close, err := getproviders.FakeInstallablePackageMeta(addr, version, getproviders.VersionList{getproviders.MustParseVersion("5.0")}, getproviders.CurrentPlatform, "")
  2753  			if err != nil {
  2754  				close()
  2755  				t.Fatalf("failed to prepare fake package for %s %s: %s", addr.ForDisplay(), versionStr, err)
  2756  			}
  2757  			closes = append(closes, close)
  2758  			packages = append(packages, meta)
  2759  		}
  2760  	}
  2761  
  2762  	return getproviders.NewMockSource(packages, nil), close
  2763  }
  2764  
  2765  // installFakeProviderPackages installs a fake package for the given provider
  2766  // names (interpreted as a "default" provider address) and versions into the
  2767  // local plugin cache for the given "meta".
  2768  //
  2769  // Any test using this must be using testChdir or some similar mechanism to
  2770  // make sure that it isn't writing directly into a test fixture or source
  2771  // directory within the codebase.
  2772  //
  2773  // If a requested package cannot be installed for some reason, this function
  2774  // will abort the test using the given testing.T. Therefore if this function
  2775  // returns the caller can assume that the requested providers have been
  2776  // installed.
  2777  func installFakeProviderPackages(t *testing.T, meta *Meta, providerVersions map[string][]string) {
  2778  	t.Helper()
  2779  
  2780  	cacheDir := meta.providerLocalCacheDir()
  2781  	installFakeProviderPackagesElsewhere(t, cacheDir, providerVersions)
  2782  }
  2783  
  2784  // installFakeProviderPackagesElsewhere is a variant of installFakeProviderPackages
  2785  // that will install packages into the given provider cache directory, rather
  2786  // than forcing the use of the local cache of the current "Meta".
  2787  func installFakeProviderPackagesElsewhere(t *testing.T, cacheDir *providercache.Dir, providerVersions map[string][]string) {
  2788  	t.Helper()
  2789  
  2790  	// It can be hard to spot the mistake of forgetting to run testChdir before
  2791  	// modifying the working directory, so we'll use a simple heuristic here
  2792  	// to try to detect that mistake and make a noisy error about it instead.
  2793  	wd, err := os.Getwd()
  2794  	if err == nil {
  2795  		wd = filepath.Clean(wd)
  2796  		// If the directory we're in is named "command" or if we're under a
  2797  		// directory named "testdata" then we'll assume a mistake and generate
  2798  		// an error. This will cause the test to fail but won't block it from
  2799  		// running.
  2800  		if filepath.Base(wd) == "command" || filepath.Base(wd) == "testdata" || strings.Contains(filepath.ToSlash(wd), "/testdata/") {
  2801  			t.Errorf("installFakeProviderPackage may be used only by tests that switch to a temporary working directory, e.g. using testChdir")
  2802  		}
  2803  	}
  2804  
  2805  	for name, versions := range providerVersions {
  2806  		addr := addrs.NewDefaultProvider(name)
  2807  		for _, versionStr := range versions {
  2808  			version, err := getproviders.ParseVersion(versionStr)
  2809  			if err != nil {
  2810  				t.Fatalf("failed to parse %q as a version number for %q: %s", versionStr, name, err)
  2811  			}
  2812  			meta, close, err := getproviders.FakeInstallablePackageMeta(addr, version, getproviders.VersionList{getproviders.MustParseVersion("5.0")}, getproviders.CurrentPlatform, "")
  2813  			// We're going to install all these fake packages before we return,
  2814  			// so we don't need to preserve them afterwards.
  2815  			defer close()
  2816  			if err != nil {
  2817  				t.Fatalf("failed to prepare fake package for %s %s: %s", name, versionStr, err)
  2818  			}
  2819  			_, err = cacheDir.InstallPackage(context.Background(), meta, nil)
  2820  			if err != nil {
  2821  				t.Fatalf("failed to install fake package for %s %s: %s", name, versionStr, err)
  2822  			}
  2823  		}
  2824  	}
  2825  }
  2826  
  2827  // expectedPackageInstallPath is a companion to installFakeProviderPackages
  2828  // that returns the path where the provider with the given name and version
  2829  // would be installed and, relatedly, where the installer will expect to
  2830  // find an already-installed version.
  2831  //
  2832  // Just as with installFakeProviderPackages, this function is a shortcut helper
  2833  // for "default-namespaced" providers as we commonly use in tests. If you need
  2834  // more control over the provider addresses, use functions of the underlying
  2835  // getproviders and providercache packages instead.
  2836  //
  2837  // The result always uses forward slashes, even on Windows, for consistency
  2838  // with how the getproviders and providercache packages build paths.
  2839  func expectedPackageInstallPath(name, version string, exe bool) string {
  2840  	platform := getproviders.CurrentPlatform
  2841  	baseDir := ".terraform/providers"
  2842  	if exe {
  2843  		p := fmt.Sprintf("registry.terraform.io/hashicorp/%s/%s/%s/terraform-provider-%s_%s", name, version, platform, name, version)
  2844  		if platform.OS == "windows" {
  2845  			p += ".exe"
  2846  		}
  2847  		return filepath.ToSlash(filepath.Join(baseDir, p))
  2848  	}
  2849  	return filepath.ToSlash(filepath.Join(
  2850  		baseDir, fmt.Sprintf("registry.terraform.io/hashicorp/%s/%s/%s", name, version, platform),
  2851  	))
  2852  }