github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/cliconfig/cliconfig_test.go (about) 1 package cliconfig 2 3 import ( 4 "os" 5 "path/filepath" 6 "reflect" 7 "testing" 8 9 "github.com/davecgh/go-spew/spew" 10 "github.com/google/go-cmp/cmp" 11 "github.com/hashicorp/terraform/internal/tfdiags" 12 ) 13 14 // This is the directory where our test fixtures are. 15 const fixtureDir = "./testdata" 16 17 func TestLoadConfig(t *testing.T) { 18 c, err := loadConfigFile(filepath.Join(fixtureDir, "config")) 19 if err != nil { 20 t.Fatalf("err: %s", err) 21 } 22 23 expected := &Config{ 24 Providers: map[string]string{ 25 "aws": "foo", 26 "do": "bar", 27 }, 28 } 29 30 if !reflect.DeepEqual(c, expected) { 31 t.Fatalf("bad: %#v", c) 32 } 33 } 34 35 func TestLoadConfig_envSubst(t *testing.T) { 36 defer os.Unsetenv("TFTEST") 37 os.Setenv("TFTEST", "hello") 38 39 c, err := loadConfigFile(filepath.Join(fixtureDir, "config-env")) 40 if err != nil { 41 t.Fatalf("err: %s", err) 42 } 43 44 expected := &Config{ 45 Providers: map[string]string{ 46 "aws": "hello", 47 "google": "bar", 48 }, 49 Provisioners: map[string]string{ 50 "local": "hello", 51 }, 52 } 53 54 if !reflect.DeepEqual(c, expected) { 55 t.Fatalf("bad: %#v", c) 56 } 57 } 58 59 func TestLoadConfig_non_existing_file(t *testing.T) { 60 tmpDir := os.TempDir() 61 cliTmpFile := filepath.Join(tmpDir, "dev.tfrc") 62 63 os.Setenv("TF_CLI_CONFIG_FILE", cliTmpFile) 64 defer os.Unsetenv("TF_CLI_CONFIG_FILE") 65 66 c, errs := LoadConfig() 67 if errs.HasErrors() || c.Validate().HasErrors() { 68 t.Fatalf("err: %s", errs) 69 } 70 71 hasOpenFileWarn := false 72 for _, err := range errs { 73 if err.Severity() == tfdiags.Warning && err.Description().Summary == "Unable to open CLI configuration file" { 74 hasOpenFileWarn = true 75 break 76 } 77 } 78 79 if !hasOpenFileWarn { 80 t.Fatal("expecting a warning message because of nonexisting CLI configuration file") 81 } 82 } 83 84 func TestEnvConfig(t *testing.T) { 85 tests := map[string]struct { 86 env map[string]string 87 want *Config 88 }{ 89 "no environment variables": { 90 nil, 91 &Config{}, 92 }, 93 "TF_PLUGIN_CACHE_DIR=boop": { 94 map[string]string{ 95 "TF_PLUGIN_CACHE_DIR": "boop", 96 }, 97 &Config{ 98 PluginCacheDir: "boop", 99 }, 100 }, 101 "TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE=anything_except_zero": { 102 map[string]string{ 103 "TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE": "anything_except_zero", 104 }, 105 &Config{ 106 PluginCacheMayBreakDependencyLockFile: true, 107 }, 108 }, 109 "TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE=0": { 110 map[string]string{ 111 "TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE": "0", 112 }, 113 &Config{}, 114 }, 115 "TF_PLUGIN_CACHE_DIR and TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE": { 116 map[string]string{ 117 "TF_PLUGIN_CACHE_DIR": "beep", 118 "TF_PLUGIN_CACHE_MAY_BREAK_DEPENDENCY_LOCK_FILE": "1", 119 }, 120 &Config{ 121 PluginCacheDir: "beep", 122 PluginCacheMayBreakDependencyLockFile: true, 123 }, 124 }, 125 } 126 127 for name, test := range tests { 128 t.Run(name, func(t *testing.T) { 129 got := envConfig(test.env) 130 want := test.want 131 132 if diff := cmp.Diff(want, got); diff != "" { 133 t.Errorf("wrong result\n%s", diff) 134 } 135 }) 136 } 137 } 138 139 func TestMakeEnvMap(t *testing.T) { 140 tests := map[string]struct { 141 environ []string 142 want map[string]string 143 }{ 144 "nil": { 145 nil, 146 nil, 147 }, 148 "one": { 149 []string{ 150 "FOO=bar", 151 }, 152 map[string]string{ 153 "FOO": "bar", 154 }, 155 }, 156 "many": { 157 []string{ 158 "FOO=1", 159 "BAR=2", 160 "BAZ=3", 161 }, 162 map[string]string{ 163 "FOO": "1", 164 "BAR": "2", 165 "BAZ": "3", 166 }, 167 }, 168 "conflict": { 169 []string{ 170 "FOO=1", 171 "BAR=1", 172 "FOO=2", 173 }, 174 map[string]string{ 175 "BAR": "1", 176 "FOO": "2", // Last entry of each name wins 177 }, 178 }, 179 "empty_val": { 180 []string{ 181 "FOO=", 182 }, 183 map[string]string{ 184 "FOO": "", 185 }, 186 }, 187 "no_equals": { 188 []string{ 189 "FOO=bar", 190 "INVALID", 191 }, 192 map[string]string{ 193 "FOO": "bar", 194 }, 195 }, 196 "multi_equals": { 197 []string{ 198 "FOO=bar=baz=boop", 199 }, 200 map[string]string{ 201 "FOO": "bar=baz=boop", 202 }, 203 }, 204 } 205 206 for name, test := range tests { 207 t.Run(name, func(t *testing.T) { 208 got := makeEnvMap(test.environ) 209 want := test.want 210 211 if diff := cmp.Diff(want, got); diff != "" { 212 t.Errorf("wrong result\n%s", diff) 213 } 214 }) 215 } 216 217 } 218 219 func TestLoadConfig_hosts(t *testing.T) { 220 got, diags := loadConfigFile(filepath.Join(fixtureDir, "hosts")) 221 if len(diags) != 0 { 222 t.Fatalf("%s", diags.Err()) 223 } 224 225 want := &Config{ 226 Hosts: map[string]*ConfigHost{ 227 "example.com": { 228 Services: map[string]interface{}{ 229 "modules.v1": "https://example.com/", 230 }, 231 }, 232 }, 233 } 234 235 if !reflect.DeepEqual(got, want) { 236 t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) 237 } 238 } 239 240 func TestLoadConfig_credentials(t *testing.T) { 241 got, err := loadConfigFile(filepath.Join(fixtureDir, "credentials")) 242 if err != nil { 243 t.Fatal(err) 244 } 245 246 want := &Config{ 247 Credentials: map[string]map[string]interface{}{ 248 "example.com": map[string]interface{}{ 249 "token": "foo the bar baz", 250 }, 251 "example.net": map[string]interface{}{ 252 "username": "foo", 253 "password": "baz", 254 }, 255 }, 256 CredentialsHelpers: map[string]*ConfigCredentialsHelper{ 257 "foo": &ConfigCredentialsHelper{ 258 Args: []string{"bar", "baz"}, 259 }, 260 }, 261 } 262 263 if !reflect.DeepEqual(got, want) { 264 t.Errorf("wrong result\ngot: %swant: %s", spew.Sdump(got), spew.Sdump(want)) 265 } 266 } 267 268 func TestConfigValidate(t *testing.T) { 269 tests := map[string]struct { 270 Config *Config 271 DiagCount int 272 }{ 273 "nil": { 274 nil, 275 0, 276 }, 277 "empty": { 278 &Config{}, 279 0, 280 }, 281 "host good": { 282 &Config{ 283 Hosts: map[string]*ConfigHost{ 284 "example.com": {}, 285 }, 286 }, 287 0, 288 }, 289 "host with bad hostname": { 290 &Config{ 291 Hosts: map[string]*ConfigHost{ 292 "example..com": {}, 293 }, 294 }, 295 1, // host block has invalid hostname 296 }, 297 "credentials good": { 298 &Config{ 299 Credentials: map[string]map[string]interface{}{ 300 "example.com": map[string]interface{}{ 301 "token": "foo", 302 }, 303 }, 304 }, 305 0, 306 }, 307 "credentials with bad hostname": { 308 &Config{ 309 Credentials: map[string]map[string]interface{}{ 310 "example..com": map[string]interface{}{ 311 "token": "foo", 312 }, 313 }, 314 }, 315 1, // credentials block has invalid hostname 316 }, 317 "credentials helper good": { 318 &Config{ 319 CredentialsHelpers: map[string]*ConfigCredentialsHelper{ 320 "foo": {}, 321 }, 322 }, 323 0, 324 }, 325 "credentials helper too many": { 326 &Config{ 327 CredentialsHelpers: map[string]*ConfigCredentialsHelper{ 328 "foo": {}, 329 "bar": {}, 330 }, 331 }, 332 1, // no more than one credentials_helper block allowed 333 }, 334 "provider_installation good none": { 335 &Config{ 336 ProviderInstallation: nil, 337 }, 338 0, 339 }, 340 "provider_installation good one": { 341 &Config{ 342 ProviderInstallation: []*ProviderInstallation{ 343 {}, 344 }, 345 }, 346 0, 347 }, 348 "provider_installation too many": { 349 &Config{ 350 ProviderInstallation: []*ProviderInstallation{ 351 {}, 352 {}, 353 }, 354 }, 355 1, // no more than one provider_installation block allowed 356 }, 357 "plugin_cache_dir does not exist": { 358 &Config{ 359 PluginCacheDir: "fake", 360 }, 361 1, // The specified plugin cache dir %s cannot be opened 362 }, 363 } 364 365 for name, test := range tests { 366 t.Run(name, func(t *testing.T) { 367 diags := test.Config.Validate() 368 if len(diags) != test.DiagCount { 369 t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) 370 for _, diag := range diags { 371 t.Logf("- %#v", diag.Description()) 372 } 373 } 374 }) 375 } 376 } 377 378 func TestConfig_Merge(t *testing.T) { 379 c1 := &Config{ 380 Providers: map[string]string{ 381 "foo": "bar", 382 "bar": "blah", 383 }, 384 Provisioners: map[string]string{ 385 "local": "local", 386 "remote": "bad", 387 }, 388 Hosts: map[string]*ConfigHost{ 389 "example.com": { 390 Services: map[string]interface{}{ 391 "modules.v1": "http://example.com/", 392 }, 393 }, 394 }, 395 Credentials: map[string]map[string]interface{}{ 396 "foo": { 397 "bar": "baz", 398 }, 399 }, 400 CredentialsHelpers: map[string]*ConfigCredentialsHelper{ 401 "buz": {}, 402 }, 403 ProviderInstallation: []*ProviderInstallation{ 404 { 405 Methods: []*ProviderInstallationMethod{ 406 {Location: ProviderInstallationFilesystemMirror("a")}, 407 {Location: ProviderInstallationFilesystemMirror("b")}, 408 }, 409 }, 410 { 411 Methods: []*ProviderInstallationMethod{ 412 {Location: ProviderInstallationFilesystemMirror("c")}, 413 }, 414 }, 415 }, 416 } 417 418 c2 := &Config{ 419 Providers: map[string]string{ 420 "bar": "baz", 421 "baz": "what", 422 }, 423 Provisioners: map[string]string{ 424 "remote": "remote", 425 }, 426 Hosts: map[string]*ConfigHost{ 427 "example.net": { 428 Services: map[string]interface{}{ 429 "modules.v1": "https://example.net/", 430 }, 431 }, 432 }, 433 Credentials: map[string]map[string]interface{}{ 434 "fee": { 435 "bur": "bez", 436 }, 437 }, 438 CredentialsHelpers: map[string]*ConfigCredentialsHelper{ 439 "biz": {}, 440 }, 441 ProviderInstallation: []*ProviderInstallation{ 442 { 443 Methods: []*ProviderInstallationMethod{ 444 {Location: ProviderInstallationFilesystemMirror("d")}, 445 }, 446 }, 447 }, 448 PluginCacheMayBreakDependencyLockFile: true, 449 } 450 451 expected := &Config{ 452 Providers: map[string]string{ 453 "foo": "bar", 454 "bar": "baz", 455 "baz": "what", 456 }, 457 Provisioners: map[string]string{ 458 "local": "local", 459 "remote": "remote", 460 }, 461 Hosts: map[string]*ConfigHost{ 462 "example.com": { 463 Services: map[string]interface{}{ 464 "modules.v1": "http://example.com/", 465 }, 466 }, 467 "example.net": { 468 Services: map[string]interface{}{ 469 "modules.v1": "https://example.net/", 470 }, 471 }, 472 }, 473 Credentials: map[string]map[string]interface{}{ 474 "foo": { 475 "bar": "baz", 476 }, 477 "fee": { 478 "bur": "bez", 479 }, 480 }, 481 CredentialsHelpers: map[string]*ConfigCredentialsHelper{ 482 "buz": {}, 483 "biz": {}, 484 }, 485 ProviderInstallation: []*ProviderInstallation{ 486 { 487 Methods: []*ProviderInstallationMethod{ 488 {Location: ProviderInstallationFilesystemMirror("a")}, 489 {Location: ProviderInstallationFilesystemMirror("b")}, 490 }, 491 }, 492 { 493 Methods: []*ProviderInstallationMethod{ 494 {Location: ProviderInstallationFilesystemMirror("c")}, 495 }, 496 }, 497 { 498 Methods: []*ProviderInstallationMethod{ 499 {Location: ProviderInstallationFilesystemMirror("d")}, 500 }, 501 }, 502 }, 503 PluginCacheMayBreakDependencyLockFile: true, 504 } 505 506 actual := c1.Merge(c2) 507 if diff := cmp.Diff(expected, actual); diff != "" { 508 t.Fatalf("wrong result\n%s", diff) 509 } 510 } 511 512 func TestConfig_Merge_disableCheckpoint(t *testing.T) { 513 c1 := &Config{ 514 DisableCheckpoint: true, 515 } 516 517 c2 := &Config{} 518 519 expected := &Config{ 520 Providers: map[string]string{}, 521 Provisioners: map[string]string{}, 522 DisableCheckpoint: true, 523 } 524 525 actual := c1.Merge(c2) 526 if !reflect.DeepEqual(actual, expected) { 527 t.Fatalf("bad: %#v", actual) 528 } 529 } 530 531 func TestConfig_Merge_disableCheckpointSignature(t *testing.T) { 532 c1 := &Config{ 533 DisableCheckpointSignature: true, 534 } 535 536 c2 := &Config{} 537 538 expected := &Config{ 539 Providers: map[string]string{}, 540 Provisioners: map[string]string{}, 541 DisableCheckpointSignature: true, 542 } 543 544 actual := c1.Merge(c2) 545 if !reflect.DeepEqual(actual, expected) { 546 t.Fatalf("bad: %#v", actual) 547 } 548 }