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 }