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

     1  package command
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"log"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"runtime"
    12  	"sort"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/mitchellh/cli"
    17  	"github.com/zclconf/go-cty/cty"
    18  
    19  	"github.com/hashicorp/terraform/addrs"
    20  	"github.com/hashicorp/terraform/backend/local"
    21  	"github.com/hashicorp/terraform/configs"
    22  	"github.com/hashicorp/terraform/helper/copy"
    23  	"github.com/hashicorp/terraform/plugin/discovery"
    24  	"github.com/hashicorp/terraform/state"
    25  	"github.com/hashicorp/terraform/states"
    26  	"github.com/hashicorp/terraform/states/statemgr"
    27  	"github.com/hashicorp/terraform/terraform"
    28  	"github.com/hashicorp/terraform/tfdiags"
    29  )
    30  
    31  func TestInit_empty(t *testing.T) {
    32  	// Create a temporary working directory that is empty
    33  	td := tempDir(t)
    34  	os.MkdirAll(td, 0755)
    35  	defer os.RemoveAll(td)
    36  	defer testChdir(t, td)()
    37  
    38  	ui := new(cli.MockUi)
    39  	c := &InitCommand{
    40  		Meta: Meta{
    41  			testingOverrides: metaOverridesForProvider(testProvider()),
    42  			Ui:               ui,
    43  		},
    44  	}
    45  
    46  	args := []string{}
    47  	if code := c.Run(args); code != 0 {
    48  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
    49  	}
    50  }
    51  
    52  func TestInit_multipleArgs(t *testing.T) {
    53  	ui := new(cli.MockUi)
    54  	c := &InitCommand{
    55  		Meta: Meta{
    56  			testingOverrides: metaOverridesForProvider(testProvider()),
    57  			Ui:               ui,
    58  		},
    59  	}
    60  
    61  	args := []string{
    62  		"bad",
    63  		"bad",
    64  	}
    65  	if code := c.Run(args); code != 1 {
    66  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
    67  	}
    68  }
    69  
    70  func TestInit_fromModule_explicitDest(t *testing.T) {
    71  	dir := tempDir(t)
    72  	err := os.Mkdir(dir, os.ModePerm)
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	ui := new(cli.MockUi)
    78  	c := &InitCommand{
    79  		Meta: Meta{
    80  			testingOverrides: metaOverridesForProvider(testProvider()),
    81  			Ui:               ui,
    82  		},
    83  	}
    84  
    85  	if _, err := os.Stat(DefaultStateFilename); err == nil {
    86  		// This should never happen; it indicates a bug in another test
    87  		// is causing a terraform.tfstate to get left behind in our directory
    88  		// here, which can interfere with our init process in a way that
    89  		// isn't relevant to this test.
    90  		fullPath, _ := filepath.Abs(DefaultStateFilename)
    91  		t.Fatalf("some other test has left terraform.tfstate behind:\n%s", fullPath)
    92  	}
    93  
    94  	args := []string{
    95  		"-from-module=" + testFixturePath("init"),
    96  		dir,
    97  	}
    98  	if code := c.Run(args); code != 0 {
    99  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   100  	}
   101  
   102  	if _, err := os.Stat(filepath.Join(dir, "hello.tf")); err != nil {
   103  		t.Fatalf("err: %s", err)
   104  	}
   105  }
   106  
   107  func TestInit_fromModule_cwdDest(t *testing.T) {
   108  	// Create a temporary working directory that is empty
   109  	td := tempDir(t)
   110  	os.MkdirAll(td, os.ModePerm)
   111  	defer os.RemoveAll(td)
   112  	defer testChdir(t, td)()
   113  
   114  	ui := new(cli.MockUi)
   115  	c := &InitCommand{
   116  		Meta: Meta{
   117  			testingOverrides: metaOverridesForProvider(testProvider()),
   118  			Ui:               ui,
   119  		},
   120  	}
   121  
   122  	args := []string{
   123  		"-from-module=" + testFixturePath("init"),
   124  	}
   125  	if code := c.Run(args); code != 0 {
   126  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   127  	}
   128  
   129  	if _, err := os.Stat(filepath.Join(td, "hello.tf")); err != nil {
   130  		t.Fatalf("err: %s", err)
   131  	}
   132  }
   133  
   134  // https://github.com/hashicorp/terraform/issues/518
   135  func TestInit_fromModule_dstInSrc(t *testing.T) {
   136  	dir := tempDir(t)
   137  	if err := os.MkdirAll(dir, 0755); err != nil {
   138  		t.Fatalf("err: %s", err)
   139  	}
   140  
   141  	// Change to the temporary directory
   142  	cwd, err := os.Getwd()
   143  	if err != nil {
   144  		t.Fatalf("err: %s", err)
   145  	}
   146  	if err := os.Chdir(dir); err != nil {
   147  		t.Fatalf("err: %s", err)
   148  	}
   149  	defer os.Chdir(cwd)
   150  
   151  	if err := os.Mkdir("foo", os.ModePerm); err != nil {
   152  		t.Fatal(err)
   153  	}
   154  
   155  	if _, err := os.Create("issue518.tf"); err != nil {
   156  		t.Fatalf("err: %s", err)
   157  	}
   158  
   159  	ui := new(cli.MockUi)
   160  	c := &InitCommand{
   161  		Meta: Meta{
   162  			testingOverrides: metaOverridesForProvider(testProvider()),
   163  			Ui:               ui,
   164  		},
   165  	}
   166  
   167  	args := []string{
   168  		"-from-module=.",
   169  		"foo",
   170  	}
   171  	if code := c.Run(args); code != 0 {
   172  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   173  	}
   174  
   175  	if _, err := os.Stat(filepath.Join(dir, "foo", "issue518.tf")); err != nil {
   176  		t.Fatalf("err: %s", err)
   177  	}
   178  }
   179  
   180  func TestInit_get(t *testing.T) {
   181  	// Create a temporary working directory that is empty
   182  	td := tempDir(t)
   183  	copy.CopyDir(testFixturePath("init-get"), td)
   184  	defer os.RemoveAll(td)
   185  	defer testChdir(t, td)()
   186  
   187  	ui := new(cli.MockUi)
   188  	c := &InitCommand{
   189  		Meta: Meta{
   190  			testingOverrides: metaOverridesForProvider(testProvider()),
   191  			Ui:               ui,
   192  		},
   193  	}
   194  
   195  	args := []string{}
   196  	if code := c.Run(args); code != 0 {
   197  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   198  	}
   199  
   200  	// Check output
   201  	output := ui.OutputWriter.String()
   202  	if !strings.Contains(output, "foo in foo") {
   203  		t.Fatalf("doesn't look like we installed module 'foo': %s", output)
   204  	}
   205  }
   206  
   207  func TestInit_getUpgradeModules(t *testing.T) {
   208  	// Create a temporary working directory that is empty
   209  	td := tempDir(t)
   210  	os.MkdirAll(td, 0755)
   211  	// copy.CopyDir(testFixturePath("init-get"), td)
   212  	defer os.RemoveAll(td)
   213  	defer testChdir(t, td)()
   214  
   215  	ui := new(cli.MockUi)
   216  	c := &InitCommand{
   217  		Meta: Meta{
   218  			testingOverrides: metaOverridesForProvider(testProvider()),
   219  			Ui:               ui,
   220  		},
   221  	}
   222  
   223  	args := []string{
   224  		"-get=true",
   225  		"-get-plugins=false",
   226  		"-upgrade",
   227  		testFixturePath("init-get"),
   228  	}
   229  	if code := c.Run(args); code != 0 {
   230  		t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String())
   231  	}
   232  
   233  	// Check output
   234  	output := ui.OutputWriter.String()
   235  	if !strings.Contains(output, "Upgrading modules...") {
   236  		t.Fatalf("doesn't look like get upgrade: %s", output)
   237  	}
   238  }
   239  
   240  func TestInit_backend(t *testing.T) {
   241  	// Create a temporary working directory that is empty
   242  	td := tempDir(t)
   243  	copy.CopyDir(testFixturePath("init-backend"), td)
   244  	defer os.RemoveAll(td)
   245  	defer testChdir(t, td)()
   246  
   247  	ui := new(cli.MockUi)
   248  	c := &InitCommand{
   249  		Meta: Meta{
   250  			testingOverrides: metaOverridesForProvider(testProvider()),
   251  			Ui:               ui,
   252  		},
   253  	}
   254  
   255  	args := []string{}
   256  	if code := c.Run(args); code != 0 {
   257  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   258  	}
   259  
   260  	if _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)); err != nil {
   261  		t.Fatalf("err: %s", err)
   262  	}
   263  }
   264  
   265  func TestInit_backendUnset(t *testing.T) {
   266  	// Create a temporary working directory that is empty
   267  	td := tempDir(t)
   268  	copy.CopyDir(testFixturePath("init-backend"), td)
   269  	defer os.RemoveAll(td)
   270  	defer testChdir(t, td)()
   271  
   272  	{
   273  		log.Printf("[TRACE] TestInit_backendUnset: beginning first init")
   274  
   275  		ui := cli.NewMockUi()
   276  		c := &InitCommand{
   277  			Meta: Meta{
   278  				testingOverrides: metaOverridesForProvider(testProvider()),
   279  				Ui:               ui,
   280  			},
   281  		}
   282  
   283  		// Init
   284  		args := []string{}
   285  		if code := c.Run(args); code != 0 {
   286  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   287  		}
   288  		log.Printf("[TRACE] TestInit_backendUnset: first init complete")
   289  		t.Logf("First run output:\n%s", ui.OutputWriter.String())
   290  		t.Logf("First run errors:\n%s", ui.ErrorWriter.String())
   291  
   292  		if _, err := os.Stat(filepath.Join(DefaultDataDir, DefaultStateFilename)); err != nil {
   293  			t.Fatalf("err: %s", err)
   294  		}
   295  	}
   296  
   297  	{
   298  		log.Printf("[TRACE] TestInit_backendUnset: beginning second init")
   299  
   300  		// Unset
   301  		if err := ioutil.WriteFile("main.tf", []byte(""), 0644); err != nil {
   302  			t.Fatalf("err: %s", err)
   303  		}
   304  
   305  		ui := cli.NewMockUi()
   306  		c := &InitCommand{
   307  			Meta: Meta{
   308  				testingOverrides: metaOverridesForProvider(testProvider()),
   309  				Ui:               ui,
   310  			},
   311  		}
   312  
   313  		args := []string{"-force-copy"}
   314  		if code := c.Run(args); code != 0 {
   315  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   316  		}
   317  		log.Printf("[TRACE] TestInit_backendUnset: second init complete")
   318  		t.Logf("Second run output:\n%s", ui.OutputWriter.String())
   319  		t.Logf("Second run errors:\n%s", ui.ErrorWriter.String())
   320  
   321  		s := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   322  		if !s.Backend.Empty() {
   323  			t.Fatal("should not have backend config")
   324  		}
   325  	}
   326  }
   327  
   328  func TestInit_backendConfigFile(t *testing.T) {
   329  	// Create a temporary working directory that is empty
   330  	td := tempDir(t)
   331  	copy.CopyDir(testFixturePath("init-backend-config-file"), td)
   332  	defer os.RemoveAll(td)
   333  	defer testChdir(t, td)()
   334  
   335  	ui := new(cli.MockUi)
   336  	c := &InitCommand{
   337  		Meta: Meta{
   338  			testingOverrides: metaOverridesForProvider(testProvider()),
   339  			Ui:               ui,
   340  		},
   341  	}
   342  
   343  	args := []string{"-backend-config", "input.config"}
   344  	if code := c.Run(args); code != 0 {
   345  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   346  	}
   347  
   348  	// Read our saved backend config and verify we have our settings
   349  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   350  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   351  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   352  	}
   353  }
   354  
   355  func TestInit_backendConfigFileChange(t *testing.T) {
   356  	// Create a temporary working directory that is empty
   357  	td := tempDir(t)
   358  	copy.CopyDir(testFixturePath("init-backend-config-file-change"), td)
   359  	defer os.RemoveAll(td)
   360  	defer testChdir(t, td)()
   361  
   362  	// Ask input
   363  	defer testInputMap(t, map[string]string{
   364  		"backend-migrate-to-new": "no",
   365  	})()
   366  
   367  	ui := new(cli.MockUi)
   368  	c := &InitCommand{
   369  		Meta: Meta{
   370  			testingOverrides: metaOverridesForProvider(testProvider()),
   371  			Ui:               ui,
   372  		},
   373  	}
   374  
   375  	args := []string{"-backend-config", "input.config"}
   376  	if code := c.Run(args); code != 0 {
   377  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   378  	}
   379  
   380  	// Read our saved backend config and verify we have our settings
   381  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   382  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   383  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   384  	}
   385  }
   386  
   387  func TestInit_backendConfigKV(t *testing.T) {
   388  	// Create a temporary working directory that is empty
   389  	td := tempDir(t)
   390  	copy.CopyDir(testFixturePath("init-backend-config-kv"), td)
   391  	defer os.RemoveAll(td)
   392  	defer testChdir(t, td)()
   393  
   394  	ui := new(cli.MockUi)
   395  	c := &InitCommand{
   396  		Meta: Meta{
   397  			testingOverrides: metaOverridesForProvider(testProvider()),
   398  			Ui:               ui,
   399  		},
   400  	}
   401  
   402  	args := []string{"-backend-config", "path=hello"}
   403  	if code := c.Run(args); code != 0 {
   404  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   405  	}
   406  
   407  	// Read our saved backend config and verify we have our settings
   408  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   409  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   410  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   411  	}
   412  }
   413  
   414  func TestInit_backendConfigKVReInit(t *testing.T) {
   415  	// Create a temporary working directory that is empty
   416  	td := tempDir(t)
   417  	copy.CopyDir(testFixturePath("init-backend-config-kv"), td)
   418  	defer os.RemoveAll(td)
   419  	defer testChdir(t, td)()
   420  
   421  	ui := new(cli.MockUi)
   422  	c := &InitCommand{
   423  		Meta: Meta{
   424  			testingOverrides: metaOverridesForProvider(testProvider()),
   425  			Ui:               ui,
   426  		},
   427  	}
   428  
   429  	args := []string{"-backend-config", "path=test"}
   430  	if code := c.Run(args); code != 0 {
   431  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   432  	}
   433  
   434  	ui = new(cli.MockUi)
   435  	c = &InitCommand{
   436  		Meta: Meta{
   437  			testingOverrides: metaOverridesForProvider(testProvider()),
   438  			Ui:               ui,
   439  		},
   440  	}
   441  
   442  	// a second init should require no changes, nor should it change the backend.
   443  	args = []string{"-input=false"}
   444  	if code := c.Run(args); code != 0 {
   445  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   446  	}
   447  
   448  	// make sure the backend is configured how we expect
   449  	configState := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   450  	cfg := map[string]interface{}{}
   451  	if err := json.Unmarshal(configState.Backend.ConfigRaw, &cfg); err != nil {
   452  		t.Fatal(err)
   453  	}
   454  	if cfg["path"] != "test" {
   455  		t.Fatalf(`expected backend path="test", got path="%v"`, cfg["path"])
   456  	}
   457  
   458  	// override the -backend-config options by settings
   459  	args = []string{"-input=false", "-backend-config", ""}
   460  	if code := c.Run(args); code != 0 {
   461  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   462  	}
   463  
   464  	// make sure the backend is configured how we expect
   465  	configState = testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   466  	cfg = map[string]interface{}{}
   467  	if err := json.Unmarshal(configState.Backend.ConfigRaw, &cfg); err != nil {
   468  		t.Fatal(err)
   469  	}
   470  	if cfg["path"] != nil {
   471  		t.Fatalf(`expected backend path="<nil>", got path="%v"`, cfg["path"])
   472  	}
   473  }
   474  
   475  func TestInit_backendConfigKVReInitWithConfigDiff(t *testing.T) {
   476  	// Create a temporary working directory that is empty
   477  	td := tempDir(t)
   478  	copy.CopyDir(testFixturePath("init-backend"), td)
   479  	defer os.RemoveAll(td)
   480  	defer testChdir(t, td)()
   481  
   482  	ui := new(cli.MockUi)
   483  	c := &InitCommand{
   484  		Meta: Meta{
   485  			testingOverrides: metaOverridesForProvider(testProvider()),
   486  			Ui:               ui,
   487  		},
   488  	}
   489  
   490  	args := []string{"-input=false"}
   491  	if code := c.Run(args); code != 0 {
   492  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   493  	}
   494  
   495  	ui = new(cli.MockUi)
   496  	c = &InitCommand{
   497  		Meta: Meta{
   498  			testingOverrides: metaOverridesForProvider(testProvider()),
   499  			Ui:               ui,
   500  		},
   501  	}
   502  
   503  	// a second init with identical config should require no changes, nor
   504  	// should it change the backend.
   505  	args = []string{"-input=false", "-backend-config", "path=foo"}
   506  	if code := c.Run(args); code != 0 {
   507  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   508  	}
   509  
   510  	// make sure the backend is configured how we expect
   511  	configState := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   512  	cfg := map[string]interface{}{}
   513  	if err := json.Unmarshal(configState.Backend.ConfigRaw, &cfg); err != nil {
   514  		t.Fatal(err)
   515  	}
   516  	if cfg["path"] != "foo" {
   517  		t.Fatalf(`expected backend path="foo", got path="%v"`, cfg["foo"])
   518  	}
   519  }
   520  
   521  func TestInit_backendCli_no_config_block(t *testing.T) {
   522  	// Create a temporary working directory that is empty
   523  	td := tempDir(t)
   524  	copy.CopyDir(testFixturePath("init"), td)
   525  	defer os.RemoveAll(td)
   526  	defer testChdir(t, td)()
   527  
   528  	ui := new(cli.MockUi)
   529  	c := &InitCommand{
   530  		Meta: Meta{
   531  			testingOverrides: metaOverridesForProvider(testProvider()),
   532  			Ui:               ui,
   533  		},
   534  	}
   535  
   536  	args := []string{"-backend-config", "path=test"}
   537  	if code := c.Run(args); code != 0 {
   538  		t.Fatalf("got exit status %d; want 0\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
   539  	}
   540  
   541  	errMsg := ui.ErrorWriter.String()
   542  	if !strings.Contains(errMsg, "Warning: Missing backend configuration") {
   543  		t.Fatal("expected missing backend block warning, got", errMsg)
   544  	}
   545  }
   546  
   547  func TestInit_targetSubdir(t *testing.T) {
   548  	// Create a temporary working directory that is empty
   549  	td := tempDir(t)
   550  	os.MkdirAll(td, 0755)
   551  	defer os.RemoveAll(td)
   552  	defer testChdir(t, td)()
   553  
   554  	// copy the source into a subdir
   555  	copy.CopyDir(testFixturePath("init-backend"), filepath.Join(td, "source"))
   556  
   557  	ui := new(cli.MockUi)
   558  	c := &InitCommand{
   559  		Meta: Meta{
   560  			testingOverrides: metaOverridesForProvider(testProvider()),
   561  			Ui:               ui,
   562  		},
   563  	}
   564  
   565  	args := []string{
   566  		"source",
   567  	}
   568  	if code := c.Run(args); code != 0 {
   569  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   570  	}
   571  
   572  	if _, err := os.Stat(filepath.Join(td, DefaultDataDir, DefaultStateFilename)); err != nil {
   573  		t.Fatalf("err: %s", err)
   574  	}
   575  
   576  	// a data directory should not have been added to out working dir
   577  	if _, err := os.Stat(filepath.Join(td, "source", DefaultDataDir)); !os.IsNotExist(err) {
   578  		t.Fatalf("err: %s", err)
   579  	}
   580  }
   581  
   582  func TestInit_backendReinitWithExtra(t *testing.T) {
   583  	td := tempDir(t)
   584  	copy.CopyDir(testFixturePath("init-backend-empty"), td)
   585  	defer os.RemoveAll(td)
   586  	defer testChdir(t, td)()
   587  
   588  	m := testMetaBackend(t, nil)
   589  	opts := &BackendOpts{
   590  		ConfigOverride: configs.SynthBody("synth", map[string]cty.Value{
   591  			"path": cty.StringVal("hello"),
   592  		}),
   593  		Init: true,
   594  	}
   595  
   596  	_, cHash, err := m.backendConfig(opts)
   597  	if err != nil {
   598  		t.Fatal(err)
   599  	}
   600  
   601  	ui := new(cli.MockUi)
   602  	c := &InitCommand{
   603  		Meta: Meta{
   604  			testingOverrides: metaOverridesForProvider(testProvider()),
   605  			Ui:               ui,
   606  		},
   607  	}
   608  
   609  	args := []string{"-backend-config", "path=hello"}
   610  	if code := c.Run(args); code != 0 {
   611  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   612  	}
   613  
   614  	// Read our saved backend config and verify we have our settings
   615  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   616  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   617  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   618  	}
   619  
   620  	if state.Backend.Hash != uint64(cHash) {
   621  		t.Fatal("mismatched state and config backend hashes")
   622  	}
   623  
   624  	// init again and make sure nothing changes
   625  	if code := c.Run(args); code != 0 {
   626  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   627  	}
   628  	state = testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   629  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"hello","workspace_dir":null}`; got != want {
   630  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   631  	}
   632  	if state.Backend.Hash != uint64(cHash) {
   633  		t.Fatal("mismatched state and config backend hashes")
   634  	}
   635  }
   636  
   637  // move option from config to -backend-config args
   638  func TestInit_backendReinitConfigToExtra(t *testing.T) {
   639  	td := tempDir(t)
   640  	copy.CopyDir(testFixturePath("init-backend"), td)
   641  	defer os.RemoveAll(td)
   642  	defer testChdir(t, td)()
   643  
   644  	ui := new(cli.MockUi)
   645  	c := &InitCommand{
   646  		Meta: Meta{
   647  			testingOverrides: metaOverridesForProvider(testProvider()),
   648  			Ui:               ui,
   649  		},
   650  	}
   651  
   652  	if code := c.Run([]string{"-input=false"}); code != 0 {
   653  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   654  	}
   655  
   656  	// Read our saved backend config and verify we have our settings
   657  	state := testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   658  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"foo","workspace_dir":null}`; got != want {
   659  		t.Errorf("wrong config\ngot:  %s\nwant: %s", got, want)
   660  	}
   661  
   662  	backendHash := state.Backend.Hash
   663  
   664  	// init again but remove the path option from the config
   665  	cfg := "terraform {\n  backend \"local\" {}\n}\n"
   666  	if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil {
   667  		t.Fatal(err)
   668  	}
   669  
   670  	// We need a fresh InitCommand here because the old one now has our configuration
   671  	// file cached inside it, so it won't re-read the modification we just made.
   672  	c = &InitCommand{
   673  		Meta: Meta{
   674  			testingOverrides: metaOverridesForProvider(testProvider()),
   675  			Ui:               ui,
   676  		},
   677  	}
   678  
   679  	args := []string{"-input=false", "-backend-config=path=foo"}
   680  	if code := c.Run(args); code != 0 {
   681  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   682  	}
   683  	state = testDataStateRead(t, filepath.Join(DefaultDataDir, DefaultStateFilename))
   684  	if got, want := normalizeJSON(t, state.Backend.ConfigRaw), `{"path":"foo","workspace_dir":null}`; got != want {
   685  		t.Errorf("wrong config after moving to arg\ngot:  %s\nwant: %s", got, want)
   686  	}
   687  
   688  	if state.Backend.Hash == backendHash {
   689  		t.Fatal("state.Backend.Hash was not updated")
   690  	}
   691  }
   692  
   693  // make sure inputFalse stops execution on migrate
   694  func TestInit_inputFalse(t *testing.T) {
   695  	td := tempDir(t)
   696  	copy.CopyDir(testFixturePath("init-backend"), td)
   697  	defer os.RemoveAll(td)
   698  	defer testChdir(t, td)()
   699  
   700  	ui := new(cli.MockUi)
   701  	c := &InitCommand{
   702  		Meta: Meta{
   703  			testingOverrides: metaOverridesForProvider(testProvider()),
   704  			Ui:               ui,
   705  		},
   706  	}
   707  
   708  	args := []string{"-input=false", "-backend-config=path=foo"}
   709  	if code := c.Run([]string{"-input=false"}); code != 0 {
   710  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
   711  	}
   712  
   713  	// write different states for foo and bar
   714  	fooState := states.BuildState(func(s *states.SyncState) {
   715  		s.SetOutputValue(
   716  			addrs.OutputValue{Name: "foo"}.Absolute(addrs.RootModuleInstance),
   717  			cty.StringVal("foo"),
   718  			false, // not sensitive
   719  		)
   720  	})
   721  	if err := statemgr.NewFilesystem("foo").WriteState(fooState); err != nil {
   722  		t.Fatal(err)
   723  	}
   724  	barState := states.BuildState(func(s *states.SyncState) {
   725  		s.SetOutputValue(
   726  			addrs.OutputValue{Name: "bar"}.Absolute(addrs.RootModuleInstance),
   727  			cty.StringVal("bar"),
   728  			false, // not sensitive
   729  		)
   730  	})
   731  	if err := statemgr.NewFilesystem("bar").WriteState(barState); err != nil {
   732  		t.Fatal(err)
   733  	}
   734  
   735  	ui = new(cli.MockUi)
   736  	c = &InitCommand{
   737  		Meta: Meta{
   738  			testingOverrides: metaOverridesForProvider(testProvider()),
   739  			Ui:               ui,
   740  		},
   741  	}
   742  
   743  	args = []string{"-input=false", "-backend-config=path=bar"}
   744  	if code := c.Run(args); code == 0 {
   745  		t.Fatal("init should have failed", ui.OutputWriter)
   746  	}
   747  
   748  	errMsg := ui.ErrorWriter.String()
   749  	if !strings.Contains(errMsg, "input disabled") {
   750  		t.Fatal("expected input disabled error, got", errMsg)
   751  	}
   752  
   753  	ui = new(cli.MockUi)
   754  	c = &InitCommand{
   755  		Meta: Meta{
   756  			testingOverrides: metaOverridesForProvider(testProvider()),
   757  			Ui:               ui,
   758  		},
   759  	}
   760  
   761  	// A missing input=false should abort rather than loop infinitely
   762  	args = []string{"-backend-config=path=baz"}
   763  	if code := c.Run(args); code == 0 {
   764  		t.Fatal("init should have failed", ui.OutputWriter)
   765  	}
   766  }
   767  
   768  func TestInit_getProvider(t *testing.T) {
   769  	// Create a temporary working directory that is empty
   770  	td := tempDir(t)
   771  	copy.CopyDir(testFixturePath("init-get-providers"), td)
   772  	defer os.RemoveAll(td)
   773  	defer testChdir(t, td)()
   774  
   775  	overrides := metaOverridesForProvider(testProvider())
   776  	ui := new(cli.MockUi)
   777  	m := Meta{
   778  		testingOverrides: overrides,
   779  		Ui:               ui,
   780  	}
   781  	installer := &mockProviderInstaller{
   782  		Providers: map[string][]string{
   783  			// looking for an exact version
   784  			"exact": []string{"1.2.3"},
   785  			// config requires >= 2.3.3
   786  			"greater_than": []string{"2.3.4", "2.3.3", "2.3.0"},
   787  			// config specifies
   788  			"between": []string{"3.4.5", "2.3.4", "1.2.3"},
   789  		},
   790  
   791  		Dir: m.pluginDir(),
   792  	}
   793  
   794  	c := &InitCommand{
   795  		Meta:              m,
   796  		providerInstaller: installer,
   797  	}
   798  
   799  	args := []string{
   800  		"-backend=false", // should be possible to install plugins without backend init
   801  	}
   802  	if code := c.Run(args); code != 0 {
   803  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   804  	}
   805  
   806  	if !installer.PurgeUnusedCalled {
   807  		t.Errorf("init didn't purge providers, but should have")
   808  	}
   809  
   810  	// check that we got the providers for our config
   811  	exactPath := filepath.Join(c.pluginDir(), installer.FileName("exact", "1.2.3"))
   812  	if _, err := os.Stat(exactPath); os.IsNotExist(err) {
   813  		t.Fatal("provider 'exact' not downloaded")
   814  	}
   815  	greaterThanPath := filepath.Join(c.pluginDir(), installer.FileName("greater_than", "2.3.4"))
   816  	if _, err := os.Stat(greaterThanPath); os.IsNotExist(err) {
   817  		t.Fatal("provider 'greater_than' not downloaded")
   818  	}
   819  	betweenPath := filepath.Join(c.pluginDir(), installer.FileName("between", "2.3.4"))
   820  	if _, err := os.Stat(betweenPath); os.IsNotExist(err) {
   821  		t.Fatal("provider 'between' not downloaded")
   822  	}
   823  
   824  	t.Run("future-state", func(t *testing.T) {
   825  		// getting providers should fail if a state from a newer version of
   826  		// terraform exists, since InitCommand.getProviders needs to inspect that
   827  		// state.
   828  		s := terraform.NewState()
   829  		s.TFVersion = "100.1.0"
   830  		local := &state.LocalState{
   831  			Path: local.DefaultStateFilename,
   832  		}
   833  		if err := local.WriteState(s); err != nil {
   834  			t.Fatal(err)
   835  		}
   836  
   837  		ui := new(cli.MockUi)
   838  		m.Ui = ui
   839  		c := &InitCommand{
   840  			Meta:              m,
   841  			providerInstaller: installer,
   842  		}
   843  
   844  		if code := c.Run(nil); code == 0 {
   845  			t.Fatal("expected error, got:", ui.OutputWriter)
   846  		}
   847  
   848  		errMsg := ui.ErrorWriter.String()
   849  		if !strings.Contains(errMsg, "which is newer than current") {
   850  			t.Fatal("unexpected error:", errMsg)
   851  		}
   852  	})
   853  }
   854  
   855  // make sure we can locate providers in various paths
   856  func TestInit_findVendoredProviders(t *testing.T) {
   857  	// Create a temporary working directory that is empty
   858  	td := tempDir(t)
   859  
   860  	configDirName := "init-get-providers"
   861  	copy.CopyDir(testFixturePath(configDirName), filepath.Join(td, configDirName))
   862  	defer os.RemoveAll(td)
   863  	defer testChdir(t, td)()
   864  
   865  	ui := new(cli.MockUi)
   866  	m := Meta{
   867  		testingOverrides: metaOverridesForProvider(testProvider()),
   868  		Ui:               ui,
   869  	}
   870  
   871  	c := &InitCommand{
   872  		Meta:              m,
   873  		providerInstaller: &mockProviderInstaller{},
   874  	}
   875  
   876  	// make our plugin paths
   877  	if err := os.MkdirAll(c.pluginDir(), 0755); err != nil {
   878  		t.Fatal(err)
   879  	}
   880  	if err := os.MkdirAll(DefaultPluginVendorDir, 0755); err != nil {
   881  		t.Fatal(err)
   882  	}
   883  
   884  	// add some dummy providers
   885  	// the auto plugin directory
   886  	exactPath := filepath.Join(c.pluginDir(), "terraform-provider-exact_v1.2.3_x4")
   887  	if err := ioutil.WriteFile(exactPath, []byte("test bin"), 0755); err != nil {
   888  		t.Fatal(err)
   889  	}
   890  	// the vendor path
   891  	greaterThanPath := filepath.Join(DefaultPluginVendorDir, "terraform-provider-greater_than_v2.3.4_x4")
   892  	if err := ioutil.WriteFile(greaterThanPath, []byte("test bin"), 0755); err != nil {
   893  		t.Fatal(err)
   894  	}
   895  	// Check the current directory too
   896  	betweenPath := filepath.Join(".", "terraform-provider-between_v2.3.4_x4")
   897  	if err := ioutil.WriteFile(betweenPath, []byte("test bin"), 0755); err != nil {
   898  		t.Fatal(err)
   899  	}
   900  
   901  	args := []string{configDirName}
   902  	if code := c.Run(args); code != 0 {
   903  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   904  	}
   905  }
   906  
   907  // make sure we can locate providers defined in the legacy rc file
   908  func TestInit_rcProviders(t *testing.T) {
   909  	// Create a temporary working directory that is empty
   910  	td := tempDir(t)
   911  
   912  	configDirName := "init-legacy-rc"
   913  	copy.CopyDir(testFixturePath(configDirName), filepath.Join(td, configDirName))
   914  	defer os.RemoveAll(td)
   915  	defer testChdir(t, td)()
   916  
   917  	pluginDir := filepath.Join(td, "custom")
   918  	pluginPath := filepath.Join(pluginDir, "terraform-provider-legacy")
   919  
   920  	ui := new(cli.MockUi)
   921  	m := Meta{
   922  		Ui: ui,
   923  		PluginOverrides: &PluginOverrides{
   924  			Providers: map[string]string{
   925  				"legacy": pluginPath,
   926  			},
   927  		},
   928  	}
   929  
   930  	c := &InitCommand{
   931  		Meta:              m,
   932  		providerInstaller: &mockProviderInstaller{},
   933  	}
   934  
   935  	// make our plugin paths
   936  	if err := os.MkdirAll(pluginDir, 0755); err != nil {
   937  		t.Fatal(err)
   938  	}
   939  
   940  	if err := ioutil.WriteFile(pluginPath, []byte("test bin"), 0755); err != nil {
   941  		t.Fatal(err)
   942  	}
   943  
   944  	args := []string{configDirName}
   945  	if code := c.Run(args); code != 0 {
   946  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   947  	}
   948  }
   949  
   950  func TestInit_providerSource(t *testing.T) {
   951  	// Create a temporary working directory that is empty
   952  	td := tempDir(t)
   953  
   954  	configDirName := "init-required-providers"
   955  	copy.CopyDir(testFixturePath(configDirName), filepath.Join(td, configDirName))
   956  	defer os.RemoveAll(td)
   957  	defer testChdir(t, td)()
   958  
   959  	ui := new(cli.MockUi)
   960  	m := Meta{
   961  		testingOverrides: metaOverridesForProvider(testProvider()),
   962  		Ui:               ui,
   963  	}
   964  
   965  	c := &InitCommand{
   966  		Meta:              m,
   967  		providerInstaller: &mockProviderInstaller{},
   968  	}
   969  
   970  	// make our plugin paths
   971  	if err := os.MkdirAll(c.pluginDir(), 0755); err != nil {
   972  		t.Fatal(err)
   973  	}
   974  	if err := os.MkdirAll(DefaultPluginVendorDir, 0755); err != nil {
   975  		t.Fatal(err)
   976  	}
   977  
   978  	// add some dummy providers
   979  	// the auto plugin directory
   980  	testPath := filepath.Join(c.pluginDir(), "terraform-provider-test_v1.2.3_x4")
   981  	if err := ioutil.WriteFile(testPath, []byte("test bin"), 0755); err != nil {
   982  		t.Fatal(err)
   983  	}
   984  	// the vendor path
   985  	sourcePath := filepath.Join(DefaultPluginVendorDir, "terraform-provider-source_v1.2.3_x4")
   986  	if err := ioutil.WriteFile(sourcePath, []byte("test bin"), 0755); err != nil {
   987  		t.Fatal(err)
   988  	}
   989  
   990  	args := []string{configDirName}
   991  
   992  	if code := c.Run(args); code != 0 {
   993  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   994  	}
   995  	if strings.Contains(ui.OutputWriter.String(), "Terraform has initialized, but configuration upgrades may be needed") {
   996  		t.Fatalf("unexpected \"configuration upgrade\" warning in output")
   997  	}
   998  }
   999  
  1000  func TestInit_getUpgradePlugins(t *testing.T) {
  1001  	// Create a temporary working directory that is empty
  1002  	td := tempDir(t)
  1003  	copy.CopyDir(testFixturePath("init-get-providers"), td)
  1004  	defer os.RemoveAll(td)
  1005  	defer testChdir(t, td)()
  1006  
  1007  	ui := new(cli.MockUi)
  1008  	m := Meta{
  1009  		testingOverrides: metaOverridesForProvider(testProvider()),
  1010  		Ui:               ui,
  1011  	}
  1012  
  1013  	installer := &mockProviderInstaller{
  1014  		Providers: map[string][]string{
  1015  			// looking for an exact version
  1016  			"exact": []string{"1.2.3"},
  1017  			// config requires >= 2.3.3
  1018  			"greater_than": []string{"2.3.4", "2.3.3", "2.3.0"},
  1019  			// config specifies
  1020  			"between": []string{"3.4.5", "2.3.4", "1.2.3"},
  1021  		},
  1022  
  1023  		Dir: m.pluginDir(),
  1024  	}
  1025  
  1026  	err := os.MkdirAll(m.pluginDir(), os.ModePerm)
  1027  	if err != nil {
  1028  		t.Fatal(err)
  1029  	}
  1030  	exactUnwanted := filepath.Join(m.pluginDir(), installer.FileName("exact", "0.0.1"))
  1031  	err = ioutil.WriteFile(exactUnwanted, []byte{}, os.ModePerm)
  1032  	if err != nil {
  1033  		t.Fatal(err)
  1034  	}
  1035  	greaterThanUnwanted := filepath.Join(m.pluginDir(), installer.FileName("greater_than", "2.3.3"))
  1036  	err = ioutil.WriteFile(greaterThanUnwanted, []byte{}, os.ModePerm)
  1037  	if err != nil {
  1038  		t.Fatal(err)
  1039  	}
  1040  	betweenOverride := installer.FileName("between", "2.3.4") // intentionally directly in cwd, and should override auto-install
  1041  	err = ioutil.WriteFile(betweenOverride, []byte{}, os.ModePerm)
  1042  	if err != nil {
  1043  		t.Fatal(err)
  1044  	}
  1045  
  1046  	c := &InitCommand{
  1047  		Meta:              m,
  1048  		providerInstaller: installer,
  1049  	}
  1050  
  1051  	args := []string{
  1052  		"-upgrade=true",
  1053  	}
  1054  	if code := c.Run(args); code != 0 {
  1055  		t.Fatalf("command did not complete successfully:\n%s", ui.ErrorWriter.String())
  1056  	}
  1057  
  1058  	files, err := ioutil.ReadDir(m.pluginDir())
  1059  	if err != nil {
  1060  		t.Fatal(err)
  1061  	}
  1062  
  1063  	if !installer.PurgeUnusedCalled {
  1064  		t.Errorf("init -upgrade didn't purge providers, but should have")
  1065  	}
  1066  
  1067  	gotFilenames := make([]string, len(files))
  1068  	for i, info := range files {
  1069  		gotFilenames[i] = info.Name()
  1070  	}
  1071  	sort.Strings(gotFilenames)
  1072  
  1073  	wantFilenames := []string{
  1074  		"lock.json",
  1075  
  1076  		// no "between" because the file in cwd overrides it
  1077  
  1078  		// The mock PurgeUnused doesn't actually purge anything, so the dir
  1079  		// includes both our old and new versions.
  1080  		"terraform-provider-exact_v0.0.1_x4",
  1081  		"terraform-provider-exact_v1.2.3_x4",
  1082  		"terraform-provider-greater_than_v2.3.3_x4",
  1083  		"terraform-provider-greater_than_v2.3.4_x4",
  1084  	}
  1085  
  1086  	if !reflect.DeepEqual(gotFilenames, wantFilenames) {
  1087  		t.Errorf("wrong directory contents after upgrade\ngot:  %#v\nwant: %#v", gotFilenames, wantFilenames)
  1088  	}
  1089  
  1090  }
  1091  
  1092  func TestInit_getProviderMissing(t *testing.T) {
  1093  	// Create a temporary working directory that is empty
  1094  	td := tempDir(t)
  1095  	copy.CopyDir(testFixturePath("init-get-providers"), td)
  1096  	defer os.RemoveAll(td)
  1097  	defer testChdir(t, td)()
  1098  
  1099  	ui := new(cli.MockUi)
  1100  	m := Meta{
  1101  		testingOverrides: metaOverridesForProvider(testProvider()),
  1102  		Ui:               ui,
  1103  	}
  1104  
  1105  	installer := &mockProviderInstaller{
  1106  		Providers: map[string][]string{
  1107  			// looking for exact version 1.2.3
  1108  			"exact": []string{"1.2.4"},
  1109  			// config requires >= 2.3.3
  1110  			"greater_than": []string{"2.3.4", "2.3.3", "2.3.0"},
  1111  			// config specifies
  1112  			"between": []string{"3.4.5", "2.3.4", "1.2.3"},
  1113  		},
  1114  
  1115  		Dir: m.pluginDir(),
  1116  	}
  1117  
  1118  	c := &InitCommand{
  1119  		Meta:              m,
  1120  		providerInstaller: installer,
  1121  	}
  1122  
  1123  	args := []string{}
  1124  	if code := c.Run(args); code == 0 {
  1125  		t.Fatalf("expceted error, got output: \n%s", ui.OutputWriter.String())
  1126  	}
  1127  
  1128  	if !strings.Contains(ui.ErrorWriter.String(), "no suitable version for provider") {
  1129  		t.Fatalf("unexpected error output: %s", ui.ErrorWriter)
  1130  	}
  1131  }
  1132  
  1133  func TestInit_getProviderHaveLegacyVersion(t *testing.T) {
  1134  	// Create a temporary working directory that is empty
  1135  	td := tempDir(t)
  1136  	copy.CopyDir(testFixturePath("init-providers-lock"), td)
  1137  	defer os.RemoveAll(td)
  1138  	defer testChdir(t, td)()
  1139  
  1140  	if err := ioutil.WriteFile("terraform-provider-test", []byte("provider bin"), 0755); err != nil {
  1141  		t.Fatal(err)
  1142  	}
  1143  
  1144  	// provider test has a version constraint in the config, which should
  1145  	// trigger the getProvider error below.
  1146  	ui := new(cli.MockUi)
  1147  	c := &InitCommand{
  1148  		Meta: Meta{
  1149  			testingOverrides: metaOverridesForProvider(testProvider()),
  1150  			Ui:               ui,
  1151  		},
  1152  		providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
  1153  			return discovery.PluginMeta{}, tfdiags.Diagnostics{}, fmt.Errorf("EXPECTED PROVIDER ERROR %s", provider)
  1154  		}),
  1155  	}
  1156  
  1157  	args := []string{}
  1158  	if code := c.Run(args); code == 0 {
  1159  		t.Fatalf("expceted error, got output: \n%s", ui.OutputWriter.String())
  1160  	}
  1161  
  1162  	if !strings.Contains(ui.ErrorWriter.String(), "EXPECTED PROVIDER ERROR test") {
  1163  		t.Fatalf("unexpected error output: %s", ui.ErrorWriter)
  1164  	}
  1165  }
  1166  
  1167  func TestInit_checkRequiredVersion(t *testing.T) {
  1168  	// Create a temporary working directory that is empty
  1169  	td := tempDir(t)
  1170  	copy.CopyDir(testFixturePath("init-check-required-version"), td)
  1171  	defer os.RemoveAll(td)
  1172  	defer testChdir(t, td)()
  1173  
  1174  	ui := cli.NewMockUi()
  1175  	c := &InitCommand{
  1176  		Meta: Meta{
  1177  			testingOverrides: metaOverridesForProvider(testProvider()),
  1178  			Ui:               ui,
  1179  		},
  1180  	}
  1181  
  1182  	args := []string{}
  1183  	if code := c.Run(args); code != 1 {
  1184  		t.Fatalf("got exit status %d; want 1\nstderr:\n%s\n\nstdout:\n%s", code, ui.ErrorWriter.String(), ui.OutputWriter.String())
  1185  	}
  1186  }
  1187  
  1188  func TestInit_providerLockFile(t *testing.T) {
  1189  	// Create a temporary working directory that is empty
  1190  	td := tempDir(t)
  1191  	copy.CopyDir(testFixturePath("init-provider-lock-file"), td)
  1192  	defer os.RemoveAll(td)
  1193  	defer testChdir(t, td)()
  1194  
  1195  	ui := new(cli.MockUi)
  1196  	m := Meta{
  1197  		testingOverrides: metaOverridesForProvider(testProvider()),
  1198  		Ui:               ui,
  1199  	}
  1200  
  1201  	installer := &mockProviderInstaller{
  1202  		Providers: map[string][]string{
  1203  			"test": []string{"1.2.3"},
  1204  		},
  1205  
  1206  		Dir: m.pluginDir(),
  1207  	}
  1208  
  1209  	c := &InitCommand{
  1210  		Meta:              m,
  1211  		providerInstaller: installer,
  1212  	}
  1213  
  1214  	args := []string{}
  1215  	if code := c.Run(args); code != 0 {
  1216  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  1217  	}
  1218  
  1219  	providersLockFile := fmt.Sprintf(
  1220  		".terraform/plugins/%s_%s/lock.json",
  1221  		runtime.GOOS, runtime.GOARCH,
  1222  	)
  1223  	buf, err := ioutil.ReadFile(providersLockFile)
  1224  	if err != nil {
  1225  		t.Fatalf("failed to read providers lock file %s: %s", providersLockFile, err)
  1226  	}
  1227  	// The hash in here is for the empty files that mockGetProvider produces
  1228  	wantLockFile := strings.TrimSpace(`
  1229  {
  1230    "test": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
  1231  }
  1232  `)
  1233  	if string(buf) != wantLockFile {
  1234  		t.Errorf("wrong provider lock file contents\ngot:  %s\nwant: %s", buf, wantLockFile)
  1235  	}
  1236  }
  1237  
  1238  func TestInit_pluginDirReset(t *testing.T) {
  1239  	td := testTempDir(t)
  1240  	defer os.RemoveAll(td)
  1241  	defer testChdir(t, td)()
  1242  
  1243  	ui := new(cli.MockUi)
  1244  	c := &InitCommand{
  1245  		Meta: Meta{
  1246  			testingOverrides: metaOverridesForProvider(testProvider()),
  1247  			Ui:               ui,
  1248  		},
  1249  		providerInstaller: &mockProviderInstaller{},
  1250  	}
  1251  
  1252  	// make our vendor paths
  1253  	pluginPath := []string{"a", "b", "c"}
  1254  	for _, p := range pluginPath {
  1255  		if err := os.MkdirAll(p, 0755); err != nil {
  1256  			t.Fatal(err)
  1257  		}
  1258  	}
  1259  
  1260  	// run once and save the -plugin-dir
  1261  	args := []string{"-plugin-dir", "a"}
  1262  	if code := c.Run(args); code != 0 {
  1263  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  1264  	}
  1265  
  1266  	pluginDirs, err := c.loadPluginPath()
  1267  	if err != nil {
  1268  		t.Fatal(err)
  1269  	}
  1270  
  1271  	if len(pluginDirs) != 1 || pluginDirs[0] != "a" {
  1272  		t.Fatalf(`expected plugin dir ["a"], got %q`, pluginDirs)
  1273  	}
  1274  
  1275  	ui = new(cli.MockUi)
  1276  	c = &InitCommand{
  1277  		Meta: Meta{
  1278  			testingOverrides: metaOverridesForProvider(testProvider()),
  1279  			Ui:               ui,
  1280  		},
  1281  		providerInstaller: &mockProviderInstaller{},
  1282  	}
  1283  
  1284  	// make sure we remove the plugin-dir record
  1285  	args = []string{"-plugin-dir="}
  1286  	if code := c.Run(args); code != 0 {
  1287  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  1288  	}
  1289  
  1290  	pluginDirs, err = c.loadPluginPath()
  1291  	if err != nil {
  1292  		t.Fatal(err)
  1293  	}
  1294  
  1295  	if len(pluginDirs) != 0 {
  1296  		t.Fatalf("expected no plugin dirs got %q", pluginDirs)
  1297  	}
  1298  }
  1299  
  1300  // Test user-supplied -plugin-dir
  1301  func TestInit_pluginDirProviders(t *testing.T) {
  1302  	td := tempDir(t)
  1303  	copy.CopyDir(testFixturePath("init-get-providers"), td)
  1304  	defer os.RemoveAll(td)
  1305  	defer testChdir(t, td)()
  1306  
  1307  	ui := new(cli.MockUi)
  1308  	m := Meta{
  1309  		testingOverrides: metaOverridesForProvider(testProvider()),
  1310  		Ui:               ui,
  1311  	}
  1312  
  1313  	c := &InitCommand{
  1314  		Meta:              m,
  1315  		providerInstaller: &mockProviderInstaller{},
  1316  	}
  1317  
  1318  	// make our vendor paths
  1319  	pluginPath := []string{"a", "b", "c"}
  1320  	for _, p := range pluginPath {
  1321  		if err := os.MkdirAll(p, 0755); err != nil {
  1322  			t.Fatal(err)
  1323  		}
  1324  	}
  1325  
  1326  	// add some dummy providers in our plugin dirs
  1327  	for i, name := range []string{
  1328  		"terraform-provider-exact_v1.2.3_x4",
  1329  		"terraform-provider-greater_than_v2.3.4_x4",
  1330  		"terraform-provider-between_v2.3.4_x4",
  1331  	} {
  1332  
  1333  		if err := ioutil.WriteFile(filepath.Join(pluginPath[i], name), []byte("test bin"), 0755); err != nil {
  1334  			t.Fatal(err)
  1335  		}
  1336  	}
  1337  
  1338  	args := []string{
  1339  		"-plugin-dir", "a",
  1340  		"-plugin-dir", "b",
  1341  		"-plugin-dir", "c",
  1342  	}
  1343  	if code := c.Run(args); code != 0 {
  1344  		t.Fatalf("bad: \n%s", ui.ErrorWriter)
  1345  	}
  1346  }
  1347  
  1348  // Test user-supplied -plugin-dir doesn't allow auto-install
  1349  func TestInit_pluginDirProvidersDoesNotGet(t *testing.T) {
  1350  	td := tempDir(t)
  1351  	copy.CopyDir(testFixturePath("init-get-providers"), td)
  1352  	defer os.RemoveAll(td)
  1353  	defer testChdir(t, td)()
  1354  
  1355  	ui := new(cli.MockUi)
  1356  	m := Meta{
  1357  		testingOverrides: metaOverridesForProvider(testProvider()),
  1358  		Ui:               ui,
  1359  	}
  1360  
  1361  	c := &InitCommand{
  1362  		Meta: m,
  1363  		providerInstaller: callbackPluginInstaller(func(provider string, req discovery.Constraints) (discovery.PluginMeta, tfdiags.Diagnostics, error) {
  1364  			t.Fatalf("plugin installer should not have been called for %q", provider)
  1365  			return discovery.PluginMeta{}, tfdiags.Diagnostics{}, nil
  1366  		}),
  1367  	}
  1368  
  1369  	// make our vendor paths
  1370  	pluginPath := []string{"a", "b"}
  1371  	for _, p := range pluginPath {
  1372  		if err := os.MkdirAll(p, 0755); err != nil {
  1373  			t.Fatal(err)
  1374  		}
  1375  	}
  1376  
  1377  	// add some dummy providers in our plugin dirs
  1378  	for i, name := range []string{
  1379  		"terraform-provider-exact_v1.2.3_x4",
  1380  		"terraform-provider-greater_than_v2.3.4_x4",
  1381  	} {
  1382  
  1383  		if err := ioutil.WriteFile(filepath.Join(pluginPath[i], name), []byte("test bin"), 0755); err != nil {
  1384  			t.Fatal(err)
  1385  		}
  1386  	}
  1387  
  1388  	args := []string{
  1389  		"-plugin-dir", "a",
  1390  		"-plugin-dir", "b",
  1391  	}
  1392  	if code := c.Run(args); code == 0 {
  1393  		// should have been an error
  1394  		t.Fatalf("bad: \n%s", ui.OutputWriter)
  1395  	}
  1396  }
  1397  
  1398  // Verify that plugin-dir doesn't prevent discovery of internal providers
  1399  func TestInit_pluginWithInternal(t *testing.T) {
  1400  	td := tempDir(t)
  1401  	copy.CopyDir(testFixturePath("init-internal"), td)
  1402  	defer os.RemoveAll(td)
  1403  	defer testChdir(t, td)()
  1404  
  1405  	ui := new(cli.MockUi)
  1406  	m := Meta{
  1407  		testingOverrides: metaOverridesForProvider(testProvider()),
  1408  		Ui:               ui,
  1409  	}
  1410  
  1411  	c := &InitCommand{
  1412  		Meta: m,
  1413  	}
  1414  
  1415  	args := []string{"-plugin-dir", "./"}
  1416  	//args := []string{}
  1417  	if code := c.Run(args); code != 0 {
  1418  		t.Fatalf("error: %s", ui.ErrorWriter)
  1419  	}
  1420  }
  1421  
  1422  func TestInit_012UpgradeNeeded(t *testing.T) {
  1423  	td := tempDir(t)
  1424  	copy.CopyDir(testFixturePath("init-012upgrade"), td)
  1425  	defer os.RemoveAll(td)
  1426  	defer testChdir(t, td)()
  1427  
  1428  	ui := cli.NewMockUi()
  1429  	m := Meta{
  1430  		testingOverrides: metaOverridesForProvider(testProvider()),
  1431  		Ui:               ui,
  1432  	}
  1433  
  1434  	installer := &mockProviderInstaller{
  1435  		Providers: map[string][]string{
  1436  			"null": []string{"1.0.0"},
  1437  		},
  1438  		Dir: m.pluginDir(),
  1439  	}
  1440  
  1441  	c := &InitCommand{
  1442  		Meta:              m,
  1443  		providerInstaller: installer,
  1444  	}
  1445  
  1446  	args := []string{}
  1447  	if code := c.Run(args); code != 0 {
  1448  		t.Errorf("wrong exit status %d; want 0\nerror output:\n%s", code, ui.ErrorWriter.String())
  1449  	}
  1450  
  1451  	output := ui.OutputWriter.String()
  1452  	if !strings.Contains(output, "terraform 0.12upgrade") {
  1453  		t.Errorf("doesn't look like we detected the need for config upgrade:\n%s", output)
  1454  	}
  1455  }
  1456  
  1457  func TestInit_012UpgradeNeededInAutomation(t *testing.T) {
  1458  	td := tempDir(t)
  1459  	copy.CopyDir(testFixturePath("init-012upgrade"), td)
  1460  	defer os.RemoveAll(td)
  1461  	defer testChdir(t, td)()
  1462  
  1463  	ui := cli.NewMockUi()
  1464  	m := Meta{
  1465  		testingOverrides:    metaOverridesForProvider(testProvider()),
  1466  		Ui:                  ui,
  1467  		RunningInAutomation: true,
  1468  	}
  1469  
  1470  	installer := &mockProviderInstaller{
  1471  		Providers: map[string][]string{
  1472  			"null": []string{"1.0.0"},
  1473  		},
  1474  		Dir: m.pluginDir(),
  1475  	}
  1476  
  1477  	c := &InitCommand{
  1478  		Meta:              m,
  1479  		providerInstaller: installer,
  1480  	}
  1481  
  1482  	args := []string{}
  1483  	if code := c.Run(args); code != 0 {
  1484  		t.Errorf("wrong exit status %d; want 0\nerror output:\n%s", code, ui.ErrorWriter.String())
  1485  	}
  1486  
  1487  	output := ui.OutputWriter.String()
  1488  	if !strings.Contains(output, "Run terraform init for this configuration at a shell prompt") {
  1489  		t.Errorf("doesn't look like we instructed to run Terraform locally:\n%s", output)
  1490  	}
  1491  	if strings.Contains(output, "terraform 0.12upgrade") {
  1492  		// We don't prompt with an exact command in automation mode, since
  1493  		// the upgrade process is interactive and so it cannot be run in
  1494  		// automation.
  1495  		t.Errorf("looks like we incorrectly gave an upgrade command to run:\n%s", output)
  1496  	}
  1497  }
  1498  
  1499  func TestInit_syntaxErrorVersionSniff(t *testing.T) {
  1500  	// Create a temporary working directory that is empty
  1501  	td := tempDir(t)
  1502  	copy.CopyDir(testFixturePath("init-sniff-version-error"), td)
  1503  	defer os.RemoveAll(td)
  1504  	defer testChdir(t, td)()
  1505  
  1506  	ui := new(cli.MockUi)
  1507  	c := &InitCommand{
  1508  		Meta: Meta{
  1509  			testingOverrides: metaOverridesForProvider(testProvider()),
  1510  			Ui:               ui,
  1511  		},
  1512  	}
  1513  
  1514  	args := []string{}
  1515  	if code := c.Run(args); code != 0 {
  1516  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  1517  	}
  1518  
  1519  	// Check output.
  1520  	// Currently, this lands in the "upgrade may be needed" codepath, because
  1521  	// the intentional syntax error in our test fixture is something that
  1522  	// "terraform 0.12upgrade" could fix.
  1523  	output := ui.OutputWriter.String()
  1524  	if got, want := output, "Terraform has initialized, but configuration upgrades may be needed"; !strings.Contains(got, want) {
  1525  		t.Fatalf("wrong output\ngot:\n%s\n\nwant: message containing %q", got, want)
  1526  	}
  1527  }
  1528  
  1529  func TestInit_RequiredProviderSource(t *testing.T) {
  1530  	// Create a temporary working directory that is empty
  1531  	td := tempDir(t)
  1532  	copy.CopyDir(testFixturePath("init-provider-source"), td)
  1533  	defer os.RemoveAll(td)
  1534  	defer testChdir(t, td)()
  1535  
  1536  	ui := new(cli.MockUi)
  1537  	m := Meta{
  1538  		testingOverrides: metaOverridesForProvider(testProvider()),
  1539  		Ui:               ui,
  1540  	}
  1541  	installer := &mockProviderInstaller{
  1542  		Providers: map[string][]string{
  1543  			"test": []string{"1.2.3"},
  1544  		},
  1545  		Dir: m.pluginDir(),
  1546  	}
  1547  
  1548  	c := &InitCommand{
  1549  		Meta:              m,
  1550  		providerInstaller: installer,
  1551  	}
  1552  
  1553  	args := []string{}
  1554  	if code := c.Run(args); code != 0 {
  1555  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
  1556  	}
  1557  
  1558  	// Check output for expected warning.
  1559  	output := ui.ErrorWriter.String()
  1560  	if got, want := output, "Warning: Provider source not supported in Terraform v0.12"; !strings.Contains(got, want) {
  1561  		t.Fatalf("wrong output\ngot:\n%s\n\nwant: message containing %q", got, want)
  1562  	}
  1563  }