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