github.com/pulumi/terraform@v1.4.0/pkg/configs/module_test.go (about) 1 package configs 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/pulumi/terraform/pkg/addrs" 8 "github.com/zclconf/go-cty/cty" 9 ) 10 11 // TestNewModule_provider_fqns exercises module.gatherProviderLocalNames() 12 func TestNewModule_provider_local_name(t *testing.T) { 13 mod, diags := testModuleFromDir("testdata/providers-explicit-fqn") 14 if diags.HasErrors() { 15 t.Fatal(diags.Error()) 16 } 17 18 p := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test") 19 if name, exists := mod.ProviderLocalNames[p]; !exists { 20 t.Fatal("provider FQN foo/test not found") 21 } else { 22 if name != "foo-test" { 23 t.Fatalf("provider localname mismatch: got %s, want foo-test", name) 24 } 25 } 26 27 // ensure the reverse lookup (fqn to local name) works as well 28 localName := mod.LocalNameForProvider(p) 29 if localName != "foo-test" { 30 t.Fatal("provider local name not found") 31 } 32 33 // if there is not a local name for a provider, it should return the type name 34 localName = mod.LocalNameForProvider(addrs.NewDefaultProvider("nonexist")) 35 if localName != "nonexist" { 36 t.Error("wrong local name returned for a non-local provider") 37 } 38 39 // can also look up the "terraform" provider and see that it sources is 40 // allowed to be overridden, even though there is a builtin provider 41 // called "terraform". 42 p = addrs.NewProvider(addrs.DefaultProviderRegistryHost, "not-builtin", "not-terraform") 43 if name, exists := mod.ProviderLocalNames[p]; !exists { 44 t.Fatal("provider FQN not-builtin/not-terraform not found") 45 } else { 46 if name != "terraform" { 47 t.Fatalf("provider localname mismatch: got %s, want terraform", name) 48 } 49 } 50 } 51 52 // This test validates the provider FQNs set in each Resource 53 func TestNewModule_resource_providers(t *testing.T) { 54 cfg, diags := testNestedModuleConfigFromDir(t, "testdata/valid-modules/nested-providers-fqns") 55 if diags.HasErrors() { 56 t.Fatal(diags.Error()) 57 } 58 59 // both the root and child module have two resources, one which should use 60 // the default implied provider and one explicitly using a provider set in 61 // required_providers 62 wantImplicit := addrs.NewDefaultProvider("test") 63 wantFoo := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test") 64 wantBar := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "bar", "test") 65 66 // root module 67 if !cfg.Module.ManagedResources["test_instance.explicit"].Provider.Equals(wantFoo) { 68 t.Fatalf("wrong provider for \"test_instance.explicit\"\ngot: %s\nwant: %s", 69 cfg.Module.ManagedResources["test_instance.explicit"].Provider, 70 wantFoo, 71 ) 72 } 73 if !cfg.Module.ManagedResources["test_instance.implicit"].Provider.Equals(wantImplicit) { 74 t.Fatalf("wrong provider for \"test_instance.implicit\"\ngot: %s\nwant: %s", 75 cfg.Module.ManagedResources["test_instance.implicit"].Provider, 76 wantImplicit, 77 ) 78 } 79 80 // a data source 81 if !cfg.Module.DataResources["data.test_resource.explicit"].Provider.Equals(wantFoo) { 82 t.Fatalf("wrong provider for \"module.child.test_instance.explicit\"\ngot: %s\nwant: %s", 83 cfg.Module.ManagedResources["test_instance.explicit"].Provider, 84 wantBar, 85 ) 86 } 87 88 // child module 89 cm := cfg.Children["child"].Module 90 if !cm.ManagedResources["test_instance.explicit"].Provider.Equals(wantBar) { 91 t.Fatalf("wrong provider for \"module.child.test_instance.explicit\"\ngot: %s\nwant: %s", 92 cfg.Module.ManagedResources["test_instance.explicit"].Provider, 93 wantBar, 94 ) 95 } 96 if !cm.ManagedResources["test_instance.implicit"].Provider.Equals(wantImplicit) { 97 t.Fatalf("wrong provider for \"module.child.test_instance.implicit\"\ngot: %s\nwant: %s", 98 cfg.Module.ManagedResources["test_instance.implicit"].Provider, 99 wantImplicit, 100 ) 101 } 102 } 103 104 func TestProviderForLocalConfig(t *testing.T) { 105 mod, diags := testModuleFromDir("testdata/providers-explicit-fqn") 106 if diags.HasErrors() { 107 t.Fatal(diags.Error()) 108 } 109 lc := addrs.LocalProviderConfig{LocalName: "foo-test"} 110 got := mod.ProviderForLocalConfig(lc) 111 want := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test") 112 if !got.Equals(want) { 113 t.Fatalf("wrong result! got %#v, want %#v\n", got, want) 114 } 115 } 116 117 // At most one required_providers block per module is permitted. 118 func TestModule_required_providers_multiple(t *testing.T) { 119 _, diags := testModuleFromDir("testdata/invalid-modules/multiple-required-providers") 120 if !diags.HasErrors() { 121 t.Fatal("module should have error diags, but does not") 122 } 123 124 want := `Duplicate required providers configuration` 125 if got := diags.Error(); !strings.Contains(got, want) { 126 t.Fatalf("expected error to contain %q\nerror was:\n%s", want, got) 127 } 128 } 129 130 // A module may have required_providers configured in files loaded later than 131 // resources. These provider settings should still be reflected in the 132 // resources' configuration. 133 func TestModule_required_providers_after_resource(t *testing.T) { 134 mod, diags := testModuleFromDir("testdata/valid-modules/required-providers-after-resource") 135 if diags.HasErrors() { 136 t.Fatal(diags.Error()) 137 } 138 139 want := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "foo", "test") 140 141 req, exists := mod.ProviderRequirements.RequiredProviders["test"] 142 if !exists { 143 t.Fatal("no provider requirements found for \"test\"") 144 } 145 if req.Type != want { 146 t.Errorf("wrong provider addr for \"test\"\ngot: %s\nwant: %s", 147 req.Type, want, 148 ) 149 } 150 151 if got := mod.ManagedResources["test_instance.my-instance"].Provider; !got.Equals(want) { 152 t.Errorf("wrong provider addr for \"test_instance.my-instance\"\ngot: %s\nwant: %s", 153 got, want, 154 ) 155 } 156 } 157 158 // We support overrides for required_providers blocks, which should replace the 159 // entire block for each provider localname, leaving other blocks unaffected. 160 // This should also be reflected in any resources in the module using this 161 // provider. 162 func TestModule_required_provider_overrides(t *testing.T) { 163 mod, diags := testModuleFromDir("testdata/valid-modules/required-providers-overrides") 164 if diags.HasErrors() { 165 t.Fatal(diags.Error()) 166 } 167 168 // The foo provider and resource should be unaffected 169 want := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "acme", "foo") 170 req, exists := mod.ProviderRequirements.RequiredProviders["foo"] 171 if !exists { 172 t.Fatal("no provider requirements found for \"foo\"") 173 } 174 if req.Type != want { 175 t.Errorf("wrong provider addr for \"foo\"\ngot: %s\nwant: %s", 176 req.Type, want, 177 ) 178 } 179 if got := mod.ManagedResources["foo_thing.ft"].Provider; !got.Equals(want) { 180 t.Errorf("wrong provider addr for \"foo_thing.ft\"\ngot: %s\nwant: %s", 181 got, want, 182 ) 183 } 184 185 // The bar provider and resource should be using the override config 186 want = addrs.NewProvider(addrs.DefaultProviderRegistryHost, "blorp", "bar") 187 req, exists = mod.ProviderRequirements.RequiredProviders["bar"] 188 if !exists { 189 t.Fatal("no provider requirements found for \"bar\"") 190 } 191 if req.Type != want { 192 t.Errorf("wrong provider addr for \"bar\"\ngot: %s\nwant: %s", 193 req.Type, want, 194 ) 195 } 196 if gotVer, wantVer := req.Requirement.Required.String(), "~>2.0.0"; gotVer != wantVer { 197 t.Errorf("wrong provider version constraint for \"bar\"\ngot: %s\nwant: %s", 198 gotVer, wantVer, 199 ) 200 } 201 if got := mod.ManagedResources["bar_thing.bt"].Provider; !got.Equals(want) { 202 t.Errorf("wrong provider addr for \"bar_thing.bt\"\ngot: %s\nwant: %s", 203 got, want, 204 ) 205 } 206 } 207 208 // Resources without explicit provider configuration are assigned a provider 209 // implied based on the resource type. For example, this resource: 210 // 211 // resource "foo_instance" "test" {} 212 // 213 // ...is assigned to whichever provider has local name "foo" in the current 214 // module. 215 // 216 // To find the correct provider, we first look in the module's provider 217 // requirements map for a local name matching the resource type, and fall back 218 // to a default provider if none is found. This applies to both managed and 219 // data resources. 220 func TestModule_implied_provider(t *testing.T) { 221 mod, diags := testModuleFromDir("testdata/valid-modules/implied-providers") 222 if diags.HasErrors() { 223 t.Fatal(diags.Error()) 224 } 225 226 // The three providers used in the config resources 227 foo := addrs.NewProvider("registry.acme.corp", "acme", "foo") 228 whatever := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "acme", "something") 229 bar := addrs.NewDefaultProvider("bar") 230 231 // Verify that the registry.acme.corp/acme/foo provider is defined in the 232 // module provider requirements with local name "foo" 233 req, exists := mod.ProviderRequirements.RequiredProviders["foo"] 234 if !exists { 235 t.Fatal("no provider requirements found for \"foo\"") 236 } 237 if req.Type != foo { 238 t.Errorf("wrong provider addr for \"foo\"\ngot: %s\nwant: %s", 239 req.Type, foo, 240 ) 241 } 242 243 // Verify that the acme/something provider is defined in the 244 // module provider requirements with local name "whatever" 245 req, exists = mod.ProviderRequirements.RequiredProviders["whatever"] 246 if !exists { 247 t.Fatal("no provider requirements found for \"foo\"") 248 } 249 if req.Type != whatever { 250 t.Errorf("wrong provider addr for \"whatever\"\ngot: %s\nwant: %s", 251 req.Type, whatever, 252 ) 253 } 254 255 // Check that resources are assigned the correct providers: foo_* resources 256 // should have the custom foo provider, bar_* resources the default bar 257 // provider. 258 tests := []struct { 259 Address string 260 Provider addrs.Provider 261 }{ 262 {"foo_resource.a", foo}, 263 {"data.foo_resource.b", foo}, 264 {"bar_resource.c", bar}, 265 {"data.bar_resource.d", bar}, 266 {"whatever_resource.e", whatever}, 267 {"data.whatever_resource.f", whatever}, 268 } 269 for _, test := range tests { 270 resources := mod.ManagedResources 271 if strings.HasPrefix(test.Address, "data.") { 272 resources = mod.DataResources 273 } 274 resource, exists := resources[test.Address] 275 if !exists { 276 t.Errorf("could not find resource %q in %#v", test.Address, resources) 277 continue 278 } 279 if got := resource.Provider; !got.Equals(test.Provider) { 280 t.Errorf("wrong provider addr for %q\ngot: %s\nwant: %s", 281 test.Address, got, test.Provider, 282 ) 283 } 284 } 285 } 286 287 func TestImpliedProviderForUnqualifiedType(t *testing.T) { 288 mod, diags := testModuleFromDir("testdata/valid-modules/implied-providers") 289 if diags.HasErrors() { 290 t.Fatal(diags.Error()) 291 } 292 293 foo := addrs.NewProvider("registry.acme.corp", "acme", "foo") 294 whatever := addrs.NewProvider(addrs.DefaultProviderRegistryHost, "acme", "something") 295 bar := addrs.NewDefaultProvider("bar") 296 tf := addrs.NewBuiltInProvider("terraform") 297 298 tests := []struct { 299 Type string 300 Provider addrs.Provider 301 }{ 302 {"foo", foo}, 303 {"whatever", whatever}, 304 {"bar", bar}, 305 {"terraform", tf}, 306 } 307 for _, test := range tests { 308 got := mod.ImpliedProviderForUnqualifiedType(test.Type) 309 if !got.Equals(test.Provider) { 310 t.Errorf("wrong result for %q: got %#v, want %#v\n", test.Type, got, test.Provider) 311 } 312 } 313 } 314 315 func TestModule_backend_override(t *testing.T) { 316 mod, diags := testModuleFromDir("testdata/valid-modules/override-backend") 317 if diags.HasErrors() { 318 t.Fatal(diags.Error()) 319 } 320 321 gotType := mod.Backend.Type 322 wantType := "bar" 323 324 if gotType != wantType { 325 t.Errorf("wrong result for backend type: got %#v, want %#v\n", gotType, wantType) 326 } 327 328 attrs, _ := mod.Backend.Config.JustAttributes() 329 330 gotAttr, diags := attrs["path"].Expr.Value(nil) 331 if diags.HasErrors() { 332 t.Fatal(diags.Error()) 333 } 334 335 wantAttr := cty.StringVal("CHANGED/relative/path/to/terraform.tfstate") 336 337 if !gotAttr.RawEquals(wantAttr) { 338 t.Errorf("wrong result for backend 'path': got %#v, want %#v\n", gotAttr, wantAttr) 339 } 340 } 341 342 // Unlike most other overrides, backend blocks do not require a base configuration in a primary 343 // configuration file, as an omitted backend there implies the local backend. 344 func TestModule_backend_override_no_base(t *testing.T) { 345 mod, diags := testModuleFromDir("testdata/valid-modules/override-backend-no-base") 346 if diags.HasErrors() { 347 t.Fatal(diags.Error()) 348 } 349 350 if mod.Backend == nil { 351 t.Errorf("expected module Backend not to be nil") 352 } 353 } 354 355 func TestModule_cloud_override_backend(t *testing.T) { 356 mod, diags := testModuleFromDir("testdata/valid-modules/override-backend-with-cloud") 357 if diags.HasErrors() { 358 t.Fatal(diags.Error()) 359 } 360 361 if mod.Backend != nil { 362 t.Errorf("expected module Backend to be nil") 363 } 364 365 if mod.CloudConfig == nil { 366 t.Errorf("expected module CloudConfig not to be nil") 367 } 368 } 369 370 // Unlike most other overrides, cloud blocks do not require a base configuration in a primary 371 // configuration file, as an omitted backend there implies the local backend and cloud blocks 372 // override backends. 373 func TestModule_cloud_override_no_base(t *testing.T) { 374 mod, diags := testModuleFromDir("testdata/valid-modules/override-cloud-no-base") 375 if diags.HasErrors() { 376 t.Fatal(diags.Error()) 377 } 378 379 if mod.CloudConfig == nil { 380 t.Errorf("expected module CloudConfig not to be nil") 381 } 382 } 383 384 func TestModule_cloud_override(t *testing.T) { 385 mod, diags := testModuleFromDir("testdata/valid-modules/override-cloud") 386 if diags.HasErrors() { 387 t.Fatal(diags.Error()) 388 } 389 390 attrs, _ := mod.CloudConfig.Config.JustAttributes() 391 392 gotAttr, diags := attrs["organization"].Expr.Value(nil) 393 if diags.HasErrors() { 394 t.Fatal(diags.Error()) 395 } 396 397 wantAttr := cty.StringVal("CHANGED") 398 399 if !gotAttr.RawEquals(wantAttr) { 400 t.Errorf("wrong result for Cloud 'organization': got %#v, want %#v\n", gotAttr, wantAttr) 401 } 402 403 // The override should have completely replaced the cloud block in the primary file, no merging 404 if attrs["should_not_be_present_with_override"] != nil { 405 t.Errorf("expected 'should_not_be_present_with_override' attribute to be nil") 406 } 407 } 408 409 func TestModule_cloud_duplicate_overrides(t *testing.T) { 410 _, diags := testModuleFromDir("testdata/invalid-modules/override-cloud-duplicates") 411 want := `Duplicate Terraform Cloud configurations` 412 if got := diags.Error(); !strings.Contains(got, want) { 413 t.Fatalf("expected module error to contain %q\nerror was:\n%s", want, got) 414 } 415 }