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

     1  package e2etest
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/google/go-cmp/cmp"
    13  
    14  	"github.com/hashicorp/terraform/internal/e2e"
    15  )
    16  
    17  func TestInitProviders(t *testing.T) {
    18  	t.Parallel()
    19  
    20  	// This test reaches out to releases.hashicorp.com to download the
    21  	// template provider, so it can only run if network access is allowed.
    22  	// We intentionally don't try to stub this here, because there's already
    23  	// a stubbed version of this in the "command" package and so the goal here
    24  	// is to test the interaction with the real repository.
    25  	skipIfCannotAccessNetwork(t)
    26  
    27  	fixturePath := filepath.Join("testdata", "template-provider")
    28  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
    29  
    30  	stdout, stderr, err := tf.Run("init")
    31  	if err != nil {
    32  		t.Errorf("unexpected error: %s", err)
    33  	}
    34  
    35  	if stderr != "" {
    36  		t.Errorf("unexpected stderr output:\n%s", stderr)
    37  	}
    38  
    39  	if !strings.Contains(stdout, "Terraform has been successfully initialized!") {
    40  		t.Errorf("success message is missing from output:\n%s", stdout)
    41  	}
    42  
    43  	if !strings.Contains(stdout, "- Installing hashicorp/template v") {
    44  		t.Errorf("provider download message is missing from output:\n%s", stdout)
    45  		t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
    46  	}
    47  
    48  	if !strings.Contains(stdout, "Terraform has created a lock file") {
    49  		t.Errorf("lock file notification is missing from output:\n%s", stdout)
    50  	}
    51  
    52  }
    53  
    54  func TestInitProvidersInternal(t *testing.T) {
    55  	t.Parallel()
    56  
    57  	// This test should _not_ reach out anywhere because the "terraform"
    58  	// provider is internal to the core terraform binary.
    59  
    60  	fixturePath := filepath.Join("testdata", "terraform-provider")
    61  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
    62  
    63  	stdout, stderr, err := tf.Run("init")
    64  	if err != nil {
    65  		t.Errorf("unexpected error: %s", err)
    66  	}
    67  
    68  	if stderr != "" {
    69  		t.Errorf("unexpected stderr output:\n%s", stderr)
    70  	}
    71  
    72  	if !strings.Contains(stdout, "Terraform has been successfully initialized!") {
    73  		t.Errorf("success message is missing from output:\n%s", stdout)
    74  	}
    75  
    76  	if strings.Contains(stdout, "Installing hashicorp/terraform") {
    77  		// Shouldn't have downloaded anything with this config, because the
    78  		// provider is built in.
    79  		t.Errorf("provider download message appeared in output:\n%s", stdout)
    80  	}
    81  
    82  	if strings.Contains(stdout, "Installing terraform.io/builtin/terraform") {
    83  		// Shouldn't have downloaded anything with this config, because the
    84  		// provider is built in.
    85  		t.Errorf("provider download message appeared in output:\n%s", stdout)
    86  	}
    87  }
    88  
    89  func TestInitProvidersVendored(t *testing.T) {
    90  	t.Parallel()
    91  
    92  	// This test will try to reach out to registry.terraform.io as one of the
    93  	// possible installation locations for
    94  	// hashicorp/null, where it will find that
    95  	// versions do exist but will ultimately select the version that is
    96  	// vendored due to the version constraint.
    97  	skipIfCannotAccessNetwork(t)
    98  
    99  	fixturePath := filepath.Join("testdata", "vendored-provider")
   100  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   101  
   102  	// Our fixture dir has a generic os_arch dir, which we need to customize
   103  	// to the actual OS/arch where this test is running in order to get the
   104  	// desired result.
   105  	fixtMachineDir := tf.Path("terraform.d/plugins/registry.terraform.io/hashicorp/null/1.0.0+local/os_arch")
   106  	wantMachineDir := tf.Path("terraform.d/plugins/registry.terraform.io/hashicorp/null/1.0.0+local/", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
   107  	err := os.Rename(fixtMachineDir, wantMachineDir)
   108  	if err != nil {
   109  		t.Fatalf("unexpected error: %s", err)
   110  	}
   111  
   112  	stdout, stderr, err := tf.Run("init")
   113  	if err != nil {
   114  		t.Errorf("unexpected error: %s", err)
   115  	}
   116  
   117  	if stderr != "" {
   118  		t.Errorf("unexpected stderr output:\n%s", stderr)
   119  	}
   120  
   121  	if !strings.Contains(stdout, "Terraform has been successfully initialized!") {
   122  		t.Errorf("success message is missing from output:\n%s", stdout)
   123  	}
   124  
   125  	if !strings.Contains(stdout, "- Installing hashicorp/null v1.0.0+local") {
   126  		t.Errorf("provider download message is missing from output:\n%s", stdout)
   127  		t.Logf("(this can happen if you have a copy of the plugin in one of the global plugin search dirs)")
   128  	}
   129  
   130  }
   131  
   132  func TestInitProvidersLocalOnly(t *testing.T) {
   133  	t.Parallel()
   134  
   135  	// This test should not reach out to the network if it is behaving as
   136  	// intended. If it _does_ try to access an upstream registry and encounter
   137  	// an error doing so then that's a legitimate test failure that should be
   138  	// fixed. (If it incorrectly reaches out anywhere then it's likely to be
   139  	// to the host "example.com", which is the placeholder domain we use in
   140  	// the test fixture.)
   141  
   142  	fixturePath := filepath.Join("testdata", "local-only-provider")
   143  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   144  	// If you run this test on a workstation with a plugin-cache directory
   145  	// configured, it will leave a bad directory behind and terraform init will
   146  	// not work until you remove it.
   147  	//
   148  	// To avoid this, we will  "zero out" any existing cli config file.
   149  	tf.AddEnv("TF_CLI_CONFIG_FILE=")
   150  
   151  	// Our fixture dir has a generic os_arch dir, which we need to customize
   152  	// to the actual OS/arch where this test is running in order to get the
   153  	// desired result.
   154  	fixtMachineDir := tf.Path("terraform.d/plugins/example.com/awesomecorp/happycloud/1.2.0/os_arch")
   155  	wantMachineDir := tf.Path("terraform.d/plugins/example.com/awesomecorp/happycloud/1.2.0/", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
   156  	err := os.Rename(fixtMachineDir, wantMachineDir)
   157  	if err != nil {
   158  		t.Fatalf("unexpected error: %s", err)
   159  	}
   160  
   161  	stdout, stderr, err := tf.Run("init")
   162  	if err != nil {
   163  		t.Errorf("unexpected error: %s", err)
   164  	}
   165  
   166  	if stderr != "" {
   167  		t.Errorf("unexpected stderr output:\n%s", stderr)
   168  	}
   169  
   170  	if !strings.Contains(stdout, "Terraform has been successfully initialized!") {
   171  		t.Errorf("success message is missing from output:\n%s", stdout)
   172  	}
   173  
   174  	if !strings.Contains(stdout, "- Installing example.com/awesomecorp/happycloud v1.2.0") {
   175  		t.Errorf("provider download message is missing from output:\n%s", stdout)
   176  		t.Logf("(this can happen if you have a conflicting copy of the plugin in one of the global plugin search dirs)")
   177  	}
   178  }
   179  
   180  func TestInitProvidersCustomMethod(t *testing.T) {
   181  	t.Parallel()
   182  
   183  	// This test should not reach out to the network if it is behaving as
   184  	// intended. If it _does_ try to access an upstream registry and encounter
   185  	// an error doing so then that's a legitimate test failure that should be
   186  	// fixed. (If it incorrectly reaches out anywhere then it's likely to be
   187  	// to the host "example.com", which is the placeholder domain we use in
   188  	// the test fixture.)
   189  
   190  	for _, configFile := range []string{"cliconfig.tfrc", "cliconfig.tfrc.json"} {
   191  		t.Run(configFile, func(t *testing.T) {
   192  			fixturePath := filepath.Join("testdata", "custom-provider-install-method")
   193  			tf := e2e.NewBinary(t, terraformBin, fixturePath)
   194  
   195  			// Our fixture dir has a generic os_arch dir, which we need to customize
   196  			// to the actual OS/arch where this test is running in order to get the
   197  			// desired result.
   198  			fixtMachineDir := tf.Path("fs-mirror/example.com/awesomecorp/happycloud/1.2.0/os_arch")
   199  			wantMachineDir := tf.Path("fs-mirror/example.com/awesomecorp/happycloud/1.2.0/", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
   200  			err := os.Rename(fixtMachineDir, wantMachineDir)
   201  			if err != nil {
   202  				t.Fatalf("unexpected error: %s", err)
   203  			}
   204  
   205  			// We'll use a local CLI configuration file taken from our fixture
   206  			// directory so we can force a custom installation method config.
   207  			tf.AddEnv("TF_CLI_CONFIG_FILE=" + tf.Path(configFile))
   208  
   209  			stdout, stderr, err := tf.Run("init")
   210  			if err != nil {
   211  				t.Errorf("unexpected error: %s", err)
   212  			}
   213  
   214  			if stderr != "" {
   215  				t.Errorf("unexpected stderr output:\n%s", stderr)
   216  			}
   217  
   218  			if !strings.Contains(stdout, "Terraform has been successfully initialized!") {
   219  				t.Errorf("success message is missing from output:\n%s", stdout)
   220  			}
   221  
   222  			if !strings.Contains(stdout, "- Installing example.com/awesomecorp/happycloud v1.2.0") {
   223  				t.Errorf("provider download message is missing from output:\n%s", stdout)
   224  			}
   225  		})
   226  	}
   227  }
   228  
   229  func TestInitProviders_pluginCache(t *testing.T) {
   230  	t.Parallel()
   231  
   232  	// This test reaches out to releases.hashicorp.com to access plugin
   233  	// metadata, and download the null plugin, though the template plugin
   234  	// should come from local cache.
   235  	skipIfCannotAccessNetwork(t)
   236  
   237  	fixturePath := filepath.Join("testdata", "plugin-cache")
   238  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   239  
   240  	// Our fixture dir has a generic os_arch dir, which we need to customize
   241  	// to the actual OS/arch where this test is running in order to get the
   242  	// desired result.
   243  	fixtMachineDir := tf.Path("cache/registry.terraform.io/hashicorp/template/2.1.0/os_arch")
   244  	wantMachineDir := tf.Path("cache/registry.terraform.io/hashicorp/template/2.1.0/", fmt.Sprintf("%s_%s", runtime.GOOS, runtime.GOARCH))
   245  	err := os.Rename(fixtMachineDir, wantMachineDir)
   246  	if err != nil {
   247  		t.Fatalf("unexpected error: %s", err)
   248  	}
   249  
   250  	cmd := tf.Cmd("init")
   251  
   252  	// convert the slashes if building for windows.
   253  	p := filepath.FromSlash("./cache")
   254  	cmd.Env = append(cmd.Env, "TF_PLUGIN_CACHE_DIR="+p)
   255  	err = cmd.Run()
   256  	if err != nil {
   257  		t.Errorf("unexpected error: %s", err)
   258  	}
   259  
   260  	path := filepath.FromSlash(fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/template/2.1.0/%s_%s/terraform-provider-template_v2.1.0_x4", runtime.GOOS, runtime.GOARCH))
   261  	content, err := tf.ReadFile(path)
   262  	if err != nil {
   263  		t.Fatalf("failed to read installed plugin from %s: %s", path, err)
   264  	}
   265  	if strings.TrimSpace(string(content)) != "this is not a real plugin" {
   266  		t.Errorf("template plugin was not installed from local cache")
   267  	}
   268  
   269  	nullLinkPath := filepath.FromSlash(fmt.Sprintf(".terraform/providers/registry.terraform.io/hashicorp/null/2.1.0/%s_%s/terraform-provider-null_v2.1.0_x4", runtime.GOOS, runtime.GOARCH))
   270  	if runtime.GOOS == "windows" {
   271  		nullLinkPath = nullLinkPath + ".exe"
   272  	}
   273  	if !tf.FileExists(nullLinkPath) {
   274  		t.Errorf("null plugin was not installed into %s", nullLinkPath)
   275  	}
   276  
   277  	nullCachePath := filepath.FromSlash(fmt.Sprintf("cache/registry.terraform.io/hashicorp/null/2.1.0/%s_%s/terraform-provider-null_v2.1.0_x4", runtime.GOOS, runtime.GOARCH))
   278  	if runtime.GOOS == "windows" {
   279  		nullCachePath = nullCachePath + ".exe"
   280  	}
   281  	if !tf.FileExists(nullCachePath) {
   282  		t.Errorf("null plugin is not in cache after install. expected in: %s", nullCachePath)
   283  	}
   284  }
   285  
   286  func TestInit_fromModule(t *testing.T) {
   287  	t.Parallel()
   288  
   289  	// This test reaches out to registry.terraform.io and github.com to lookup
   290  	// and fetch a module.
   291  	skipIfCannotAccessNetwork(t)
   292  
   293  	fixturePath := filepath.Join("testdata", "empty")
   294  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   295  
   296  	cmd := tf.Cmd("init", "-from-module=hashicorp/vault/aws")
   297  	cmd.Stdin = nil
   298  	cmd.Stderr = &bytes.Buffer{}
   299  
   300  	err := cmd.Run()
   301  	if err != nil {
   302  		t.Errorf("unexpected error: %s", err)
   303  	}
   304  
   305  	stderr := cmd.Stderr.(*bytes.Buffer).String()
   306  	if stderr != "" {
   307  		t.Errorf("unexpected stderr output:\n%s", stderr)
   308  	}
   309  
   310  	content, err := tf.ReadFile("main.tf")
   311  	if err != nil {
   312  		t.Fatalf("failed to read main.tf: %s", err)
   313  	}
   314  	if !bytes.Contains(content, []byte("vault")) {
   315  		t.Fatalf("main.tf doesn't appear to be a vault configuration: \n%s", content)
   316  	}
   317  }
   318  
   319  func TestInitProviderNotFound(t *testing.T) {
   320  	t.Parallel()
   321  
   322  	// This test will reach out to registry.terraform.io as one of the possible
   323  	// installation locations for hashicorp/nonexist, which should not exist.
   324  	skipIfCannotAccessNetwork(t)
   325  
   326  	fixturePath := filepath.Join("testdata", "provider-not-found")
   327  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   328  
   329  	t.Run("registry provider not found", func(t *testing.T) {
   330  		_, stderr, err := tf.Run("init", "-no-color")
   331  		if err == nil {
   332  			t.Fatal("expected error, got success")
   333  		}
   334  
   335  		oneLineStderr := strings.ReplaceAll(stderr, "\n", " ")
   336  		if !strings.Contains(oneLineStderr, "provider registry registry.terraform.io does not have a provider named registry.terraform.io/hashicorp/nonexist") {
   337  			t.Errorf("expected error message is missing from output:\n%s", stderr)
   338  		}
   339  
   340  		if !strings.Contains(oneLineStderr, "All modules should specify their required_providers") {
   341  			t.Errorf("expected error message is missing from output:\n%s", stderr)
   342  		}
   343  	})
   344  
   345  	t.Run("local provider not found", func(t *testing.T) {
   346  		// The -plugin-dir directory must exist for the provider installer to search it.
   347  		pluginDir := tf.Path("empty")
   348  		if err := os.Mkdir(pluginDir, os.ModePerm); err != nil {
   349  			t.Fatal(err)
   350  		}
   351  
   352  		_, stderr, err := tf.Run("init", "-no-color", "-plugin-dir="+pluginDir)
   353  		if err == nil {
   354  			t.Fatal("expected error, got success")
   355  		}
   356  
   357  		if !strings.Contains(stderr, "provider registry.terraform.io/hashicorp/nonexist was not\nfound in any of the search locations\n\n  - "+pluginDir) {
   358  			t.Errorf("expected error message is missing from output:\n%s", stderr)
   359  		}
   360  	})
   361  
   362  	t.Run("special characters enabled", func(t *testing.T) {
   363  		_, stderr, err := tf.Run("init")
   364  		if err == nil {
   365  			t.Fatal("expected error, got success")
   366  		}
   367  
   368  		expectedErr := `╷
   369  │ Error: Failed to query available provider packages
   370  │` + ` ` + `
   371  │ Could not retrieve the list of available versions for provider
   372  │ hashicorp/nonexist: provider registry registry.terraform.io does not have a
   373  │ provider named registry.terraform.io/hashicorp/nonexist
   374  │ 
   375  │ All modules should specify their required_providers so that external
   376  │ consumers will get the correct providers when using a module. To see which
   377  │ modules are currently depending on hashicorp/nonexist, run the following
   378  │ command:
   379  │     terraform providers
   380  ╵
   381  
   382  `
   383  		if stripAnsi(stderr) != expectedErr {
   384  			t.Errorf("wrong output:\n%s", cmp.Diff(stripAnsi(stderr), expectedErr))
   385  		}
   386  	})
   387  }
   388  
   389  func TestInitProviderWarnings(t *testing.T) {
   390  	t.Parallel()
   391  
   392  	// This test will reach out to registry.terraform.io as one of the possible
   393  	// installation locations for hashicorp/nonexist, which should not exist.
   394  	skipIfCannotAccessNetwork(t)
   395  
   396  	fixturePath := filepath.Join("testdata", "provider-warnings")
   397  	tf := e2e.NewBinary(t, terraformBin, fixturePath)
   398  
   399  	stdout, _, err := tf.Run("init")
   400  	if err == nil {
   401  		t.Fatal("expected error, got success")
   402  	}
   403  
   404  	if !strings.Contains(stdout, "This provider is archived and no longer needed.") {
   405  		t.Errorf("expected warning message is missing from output:\n%s", stdout)
   406  	}
   407  
   408  }