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 }