github.com/kevinklinger/open_terraform@v1.3.6/noninternal/configs/config_test.go (about) 1 package configs 2 3 import ( 4 "testing" 5 6 "github.com/go-test/deep" 7 "github.com/google/go-cmp/cmp" 8 "github.com/google/go-cmp/cmp/cmpopts" 9 "github.com/zclconf/go-cty/cty" 10 11 version "github.com/hashicorp/go-version" 12 "github.com/hashicorp/hcl/v2/hclsyntax" 13 svchost "github.com/hashicorp/terraform-svchost" 14 "github.com/kevinklinger/open_terraform/noninternal/addrs" 15 "github.com/kevinklinger/open_terraform/noninternal/depsfile" 16 "github.com/kevinklinger/open_terraform/noninternal/getproviders" 17 ) 18 19 func TestConfigProviderTypes(t *testing.T) { 20 // nil cfg should return an empty map 21 got := NewEmptyConfig().ProviderTypes() 22 if len(got) != 0 { 23 t.Fatal("expected empty result from empty config") 24 } 25 26 cfg, diags := testModuleConfigFromFile("testdata/valid-files/providers-explicit-implied.tf") 27 if diags.HasErrors() { 28 t.Fatal(diags.Error()) 29 } 30 31 got = cfg.ProviderTypes() 32 want := []addrs.Provider{ 33 addrs.NewDefaultProvider("aws"), 34 addrs.NewDefaultProvider("null"), 35 addrs.NewDefaultProvider("template"), 36 addrs.NewDefaultProvider("test"), 37 } 38 for _, problem := range deep.Equal(got, want) { 39 t.Error(problem) 40 } 41 } 42 43 func TestConfigProviderTypes_nested(t *testing.T) { 44 // basic test with a nil config 45 c := NewEmptyConfig() 46 got := c.ProviderTypes() 47 if len(got) != 0 { 48 t.Fatalf("wrong result!\ngot: %#v\nwant: nil\n", got) 49 } 50 51 // config with two provider sources, and one implicit (default) provider 52 cfg, diags := testNestedModuleConfigFromDir(t, "testdata/valid-modules/nested-providers-fqns") 53 if diags.HasErrors() { 54 t.Fatal(diags.Error()) 55 } 56 57 got = cfg.ProviderTypes() 58 want := []addrs.Provider{ 59 addrs.NewProvider(addrs.DefaultProviderRegistryHost, "bar", "test"), 60 addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test"), 61 addrs.NewDefaultProvider("test"), 62 } 63 64 for _, problem := range deep.Equal(got, want) { 65 t.Error(problem) 66 } 67 } 68 69 func TestConfigResolveAbsProviderAddr(t *testing.T) { 70 cfg, diags := testModuleConfigFromDir("testdata/providers-explicit-fqn") 71 if diags.HasErrors() { 72 t.Fatal(diags.Error()) 73 } 74 75 t.Run("already absolute", func(t *testing.T) { 76 addr := addrs.AbsProviderConfig{ 77 Module: addrs.RootModule, 78 Provider: addrs.NewDefaultProvider("test"), 79 Alias: "boop", 80 } 81 got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule) 82 if got, want := got.String(), addr.String(); got != want { 83 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 84 } 85 }) 86 t.Run("local, implied mapping", func(t *testing.T) { 87 addr := addrs.LocalProviderConfig{ 88 LocalName: "implied", 89 Alias: "boop", 90 } 91 got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule) 92 want := addrs.AbsProviderConfig{ 93 Module: addrs.RootModule, 94 Provider: addrs.NewDefaultProvider("implied"), 95 Alias: "boop", 96 } 97 if got, want := got.String(), want.String(); got != want { 98 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 99 } 100 }) 101 t.Run("local, explicit mapping", func(t *testing.T) { 102 addr := addrs.LocalProviderConfig{ 103 LocalName: "foo-test", // this is explicitly set in the config 104 Alias: "boop", 105 } 106 got := cfg.ResolveAbsProviderAddr(addr, addrs.RootModule) 107 want := addrs.AbsProviderConfig{ 108 Module: addrs.RootModule, 109 Provider: addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test"), 110 Alias: "boop", 111 } 112 if got, want := got.String(), want.String(); got != want { 113 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 114 } 115 }) 116 } 117 118 func TestConfigProviderRequirements(t *testing.T) { 119 cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs") 120 // TODO: Version Constraint Deprecation. 121 // Once we've removed the version argument from provider configuration 122 // blocks, this can go back to expected 0 diagnostics. 123 // assertNoDiagnostics(t, diags) 124 assertDiagnosticCount(t, diags, 1) 125 assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated") 126 127 tlsProvider := addrs.NewProvider( 128 addrs.DefaultProviderRegistryHost, 129 "hashicorp", "tls", 130 ) 131 happycloudProvider := addrs.NewProvider( 132 svchost.Hostname("tf.example.com"), 133 "awesomecorp", "happycloud", 134 ) 135 nullProvider := addrs.NewDefaultProvider("null") 136 randomProvider := addrs.NewDefaultProvider("random") 137 impliedProvider := addrs.NewDefaultProvider("implied") 138 terraformProvider := addrs.NewBuiltInProvider("terraform") 139 configuredProvider := addrs.NewDefaultProvider("configured") 140 grandchildProvider := addrs.NewDefaultProvider("grandchild") 141 142 got, diags := cfg.ProviderRequirements() 143 assertNoDiagnostics(t, diags) 144 want := getproviders.Requirements{ 145 // the nullProvider constraints from the two modules are merged 146 nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0, 2.0.1"), 147 randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"), 148 tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"), 149 configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"), 150 impliedProvider: nil, 151 happycloudProvider: nil, 152 terraformProvider: nil, 153 grandchildProvider: nil, 154 } 155 156 if diff := cmp.Diff(want, got); diff != "" { 157 t.Errorf("wrong result\n%s", diff) 158 } 159 } 160 161 func TestConfigProviderRequirementsDuplicate(t *testing.T) { 162 _, diags := testNestedModuleConfigFromDir(t, "testdata/duplicate-local-name") 163 assertDiagnosticCount(t, diags, 3) 164 assertDiagnosticSummary(t, diags, "Duplicate required provider") 165 } 166 167 func TestConfigProviderRequirementsShallow(t *testing.T) { 168 cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs") 169 // TODO: Version Constraint Deprecation. 170 // Once we've removed the version argument from provider configuration 171 // blocks, this can go back to expected 0 diagnostics. 172 // assertNoDiagnostics(t, diags) 173 assertDiagnosticCount(t, diags, 1) 174 assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated") 175 176 tlsProvider := addrs.NewProvider( 177 addrs.DefaultProviderRegistryHost, 178 "hashicorp", "tls", 179 ) 180 nullProvider := addrs.NewDefaultProvider("null") 181 randomProvider := addrs.NewDefaultProvider("random") 182 impliedProvider := addrs.NewDefaultProvider("implied") 183 terraformProvider := addrs.NewBuiltInProvider("terraform") 184 configuredProvider := addrs.NewDefaultProvider("configured") 185 186 got, diags := cfg.ProviderRequirementsShallow() 187 assertNoDiagnostics(t, diags) 188 want := getproviders.Requirements{ 189 // the nullProvider constraint is only from the root module 190 nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0"), 191 randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"), 192 tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"), 193 configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"), 194 impliedProvider: nil, 195 terraformProvider: nil, 196 } 197 198 if diff := cmp.Diff(want, got); diff != "" { 199 t.Errorf("wrong result\n%s", diff) 200 } 201 } 202 203 func TestConfigProviderRequirementsByModule(t *testing.T) { 204 cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs") 205 // TODO: Version Constraint Deprecation. 206 // Once we've removed the version argument from provider configuration 207 // blocks, this can go back to expected 0 diagnostics. 208 // assertNoDiagnostics(t, diags) 209 assertDiagnosticCount(t, diags, 1) 210 assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated") 211 212 tlsProvider := addrs.NewProvider( 213 addrs.DefaultProviderRegistryHost, 214 "hashicorp", "tls", 215 ) 216 happycloudProvider := addrs.NewProvider( 217 svchost.Hostname("tf.example.com"), 218 "awesomecorp", "happycloud", 219 ) 220 nullProvider := addrs.NewDefaultProvider("null") 221 randomProvider := addrs.NewDefaultProvider("random") 222 impliedProvider := addrs.NewDefaultProvider("implied") 223 terraformProvider := addrs.NewBuiltInProvider("terraform") 224 configuredProvider := addrs.NewDefaultProvider("configured") 225 grandchildProvider := addrs.NewDefaultProvider("grandchild") 226 227 got, diags := cfg.ProviderRequirementsByModule() 228 assertNoDiagnostics(t, diags) 229 want := &ModuleRequirements{ 230 Name: "", 231 SourceAddr: nil, 232 SourceDir: "testdata/provider-reqs", 233 Requirements: getproviders.Requirements{ 234 // Only the root module's version is present here 235 nullProvider: getproviders.MustParseVersionConstraints("~> 2.0.0"), 236 randomProvider: getproviders.MustParseVersionConstraints("~> 1.2.0"), 237 tlsProvider: getproviders.MustParseVersionConstraints("~> 3.0"), 238 configuredProvider: getproviders.MustParseVersionConstraints("~> 1.4"), 239 impliedProvider: nil, 240 terraformProvider: nil, 241 }, 242 Children: map[string]*ModuleRequirements{ 243 "kinder": { 244 Name: "kinder", 245 SourceAddr: addrs.ModuleSourceLocal("./child"), 246 SourceDir: "testdata/provider-reqs/child", 247 Requirements: getproviders.Requirements{ 248 nullProvider: getproviders.MustParseVersionConstraints("= 2.0.1"), 249 happycloudProvider: nil, 250 }, 251 Children: map[string]*ModuleRequirements{ 252 "nested": { 253 Name: "nested", 254 SourceAddr: addrs.ModuleSourceLocal("./grandchild"), 255 SourceDir: "testdata/provider-reqs/child/grandchild", 256 Requirements: getproviders.Requirements{ 257 grandchildProvider: nil, 258 }, 259 Children: map[string]*ModuleRequirements{}, 260 }, 261 }, 262 }, 263 }, 264 } 265 266 ignore := cmpopts.IgnoreUnexported(version.Constraint{}, cty.Value{}, hclsyntax.Body{}) 267 if diff := cmp.Diff(want, got, ignore); diff != "" { 268 t.Errorf("wrong result\n%s", diff) 269 } 270 } 271 272 func TestVerifyDependencySelections(t *testing.T) { 273 cfg, diags := testNestedModuleConfigFromDir(t, "testdata/provider-reqs") 274 // TODO: Version Constraint Deprecation. 275 // Once we've removed the version argument from provider configuration 276 // blocks, this can go back to expected 0 diagnostics. 277 // assertNoDiagnostics(t, diags) 278 assertDiagnosticCount(t, diags, 1) 279 assertDiagnosticSummary(t, diags, "Version constraints inside provider configuration blocks are deprecated") 280 281 tlsProvider := addrs.NewProvider( 282 addrs.DefaultProviderRegistryHost, 283 "hashicorp", "tls", 284 ) 285 happycloudProvider := addrs.NewProvider( 286 svchost.Hostname("tf.example.com"), 287 "awesomecorp", "happycloud", 288 ) 289 nullProvider := addrs.NewDefaultProvider("null") 290 randomProvider := addrs.NewDefaultProvider("random") 291 impliedProvider := addrs.NewDefaultProvider("implied") 292 configuredProvider := addrs.NewDefaultProvider("configured") 293 grandchildProvider := addrs.NewDefaultProvider("grandchild") 294 295 tests := map[string]struct { 296 PrepareLocks func(*depsfile.Locks) 297 WantErrs []string 298 }{ 299 "empty locks": { 300 func(*depsfile.Locks) { 301 // Intentionally blank 302 }, 303 []string{ 304 `provider registry.terraform.io/hashicorp/configured: required by this configuration but no version is selected`, 305 `provider registry.terraform.io/hashicorp/grandchild: required by this configuration but no version is selected`, 306 `provider registry.terraform.io/hashicorp/implied: required by this configuration but no version is selected`, 307 `provider registry.terraform.io/hashicorp/null: required by this configuration but no version is selected`, 308 `provider registry.terraform.io/hashicorp/random: required by this configuration but no version is selected`, 309 `provider registry.terraform.io/hashicorp/tls: required by this configuration but no version is selected`, 310 `provider tf.example.com/awesomecorp/happycloud: required by this configuration but no version is selected`, 311 }, 312 }, 313 "suitable locks": { 314 func(locks *depsfile.Locks) { 315 locks.SetProvider(configuredProvider, getproviders.MustParseVersion("1.4.0"), nil, nil) 316 locks.SetProvider(grandchildProvider, getproviders.MustParseVersion("0.1.0"), nil, nil) 317 locks.SetProvider(impliedProvider, getproviders.MustParseVersion("0.2.0"), nil, nil) 318 locks.SetProvider(nullProvider, getproviders.MustParseVersion("2.0.1"), nil, nil) 319 locks.SetProvider(randomProvider, getproviders.MustParseVersion("1.2.2"), nil, nil) 320 locks.SetProvider(tlsProvider, getproviders.MustParseVersion("3.0.1"), nil, nil) 321 locks.SetProvider(happycloudProvider, getproviders.MustParseVersion("0.0.1"), nil, nil) 322 }, 323 nil, 324 }, 325 "null provider constraints changed": { 326 func(locks *depsfile.Locks) { 327 locks.SetProvider(configuredProvider, getproviders.MustParseVersion("1.4.0"), nil, nil) 328 locks.SetProvider(grandchildProvider, getproviders.MustParseVersion("0.1.0"), nil, nil) 329 locks.SetProvider(impliedProvider, getproviders.MustParseVersion("0.2.0"), nil, nil) 330 locks.SetProvider(nullProvider, getproviders.MustParseVersion("3.0.0"), nil, nil) 331 locks.SetProvider(randomProvider, getproviders.MustParseVersion("1.2.2"), nil, nil) 332 locks.SetProvider(tlsProvider, getproviders.MustParseVersion("3.0.1"), nil, nil) 333 locks.SetProvider(happycloudProvider, getproviders.MustParseVersion("0.0.1"), nil, nil) 334 }, 335 []string{ 336 `provider registry.terraform.io/hashicorp/null: locked version selection 3.0.0 doesn't match the updated version constraints "~> 2.0.0, 2.0.1"`, 337 }, 338 }, 339 "null provider lock changed": { 340 func(locks *depsfile.Locks) { 341 // In this case, we set the lock file version constraints to 342 // match the configuration, and so our error message changes 343 // to not assume the configuration changed anymore. 344 locks.SetProvider(nullProvider, getproviders.MustParseVersion("3.0.0"), getproviders.MustParseVersionConstraints("~> 2.0.0, 2.0.1"), nil) 345 346 locks.SetProvider(configuredProvider, getproviders.MustParseVersion("1.4.0"), nil, nil) 347 locks.SetProvider(grandchildProvider, getproviders.MustParseVersion("0.1.0"), nil, nil) 348 locks.SetProvider(impliedProvider, getproviders.MustParseVersion("0.2.0"), nil, nil) 349 locks.SetProvider(randomProvider, getproviders.MustParseVersion("1.2.2"), nil, nil) 350 locks.SetProvider(tlsProvider, getproviders.MustParseVersion("3.0.1"), nil, nil) 351 locks.SetProvider(happycloudProvider, getproviders.MustParseVersion("0.0.1"), nil, nil) 352 }, 353 []string{ 354 `provider registry.terraform.io/hashicorp/null: version constraints "~> 2.0.0, 2.0.1" don't match the locked version selection 3.0.0`, 355 }, 356 }, 357 "overridden provider": { 358 func(locks *depsfile.Locks) { 359 locks.SetProviderOverridden(happycloudProvider) 360 }, 361 []string{ 362 // We still catch all of the other ones, because only happycloud was overridden 363 `provider registry.terraform.io/hashicorp/configured: required by this configuration but no version is selected`, 364 `provider registry.terraform.io/hashicorp/grandchild: required by this configuration but no version is selected`, 365 `provider registry.terraform.io/hashicorp/implied: required by this configuration but no version is selected`, 366 `provider registry.terraform.io/hashicorp/null: required by this configuration but no version is selected`, 367 `provider registry.terraform.io/hashicorp/random: required by this configuration but no version is selected`, 368 `provider registry.terraform.io/hashicorp/tls: required by this configuration but no version is selected`, 369 }, 370 }, 371 } 372 373 for name, test := range tests { 374 t.Run(name, func(t *testing.T) { 375 depLocks := depsfile.NewLocks() 376 test.PrepareLocks(depLocks) 377 gotErrs := cfg.VerifyDependencySelections(depLocks) 378 379 var gotErrsStr []string 380 if gotErrs != nil { 381 gotErrsStr = make([]string, len(gotErrs)) 382 for i, err := range gotErrs { 383 gotErrsStr[i] = err.Error() 384 } 385 } 386 387 if diff := cmp.Diff(test.WantErrs, gotErrsStr); diff != "" { 388 t.Errorf("wrong errors\n%s", diff) 389 } 390 }) 391 } 392 } 393 394 func TestConfigProviderForConfigAddr(t *testing.T) { 395 cfg, diags := testModuleConfigFromDir("testdata/valid-modules/providers-fqns") 396 assertNoDiagnostics(t, diags) 397 398 got := cfg.ProviderForConfigAddr(addrs.NewDefaultLocalProviderConfig("foo-test")) 399 want := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test") 400 if !got.Equals(want) { 401 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 402 } 403 404 // now check a provider that isn't in the configuration. It should return a DefaultProvider. 405 got = cfg.ProviderForConfigAddr(addrs.NewDefaultLocalProviderConfig("bar-test")) 406 want = addrs.NewDefaultProvider("bar-test") 407 if !got.Equals(want) { 408 t.Errorf("wrong result\ngot: %s\nwant: %s", got, want) 409 } 410 } 411 412 func TestConfigAddProviderRequirements(t *testing.T) { 413 cfg, diags := testModuleConfigFromFile("testdata/valid-files/providers-explicit-implied.tf") 414 assertNoDiagnostics(t, diags) 415 416 reqs := getproviders.Requirements{ 417 addrs.NewDefaultProvider("null"): nil, 418 } 419 diags = cfg.addProviderRequirements(reqs, true) 420 assertNoDiagnostics(t, diags) 421 }