package terraform import ( "testing" "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform/addrs" "github.com/hashicorp/terraform/configs" "github.com/hashicorp/terraform/moduledeps" "github.com/hashicorp/terraform/plugin/discovery" ) func TestModuleTreeDependencies(t *testing.T) { tests := map[string]struct { ConfigDir string // directory name from testdata dir State *State Want *moduledeps.Module }{ "no config or state": { "", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{}, Children: nil, }, }, "empty config no state": { "empty", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{}, Children: nil, }, }, "explicit provider": { "module-deps-explicit-provider", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.ConstraintStr(">=1.0.0,>=2.0.0").MustParse(), Reason: moduledeps.ProviderDependencyExplicit, }, }, Children: nil, }, }, "required_providers block": { "module-deps-required-providers", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.ConstraintStr(">=1.0.0").MustParse(), Reason: moduledeps.ProviderDependencyExplicit, }, }, Children: nil, }, }, "explicit provider unconstrained": { "module-deps-explicit-provider-unconstrained", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyExplicit, }, }, Children: nil, }, }, "implicit provider": { "module-deps-implicit-provider", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyImplicit, }, }, Children: nil, }, }, "explicit provider with resource": { "module-deps-explicit-provider-resource", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.ConstraintStr(">=1.0.0").MustParse(), Reason: moduledeps.ProviderDependencyExplicit, }, }, Children: nil, }, }, "inheritance of providers": { "module-deps-inherit-provider", nil, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyExplicit, }, addrs.NewLegacyProvider("bar"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyExplicit, }, }, Children: []*moduledeps.Module{ { Name: "child", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyInherited, }, addrs.NewLegacyProvider("baz"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyImplicit, }, }, Children: []*moduledeps.Module{ { Name: "grandchild", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("bar"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyInherited, }, addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyExplicit, }, }, }, }, }, }, }, }, "provider from state": { "empty", &State{ Modules: []*ModuleState{ { Path: []string{"root"}, Resources: map[string]*ResourceState{ "foo_bar.baz": { Type: "foo_bar", Provider: "", }, }, }, }, }, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyFromState, }, }, Children: nil, }, }, "providers in both config and state": { "module-deps-explicit-provider", &State{ Modules: []*ModuleState{ { Path: []string{"root"}, Resources: map[string]*ResourceState{ "foo_bar.test1": { Type: "foo_bar", Provider: "", }, "baz_bar.test": { Type: "baz_bar", Provider: "", }, }, }, // note that we've skipped root.child intentionally here, // to verify that we'll infer it based on the following // module rather than crashing. { Path: []string{"root", "child", "grandchild"}, Resources: map[string]*ResourceState{ "banana_skin.test": { Type: "banana_skin", Provider: "", }, }, }, }, }, &moduledeps.Module{ Name: "root", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("foo"): moduledeps.ProviderDependency{ Constraints: discovery.ConstraintStr(">=1.0.0,>=2.0.0").MustParse(), Reason: moduledeps.ProviderDependencyExplicit, }, addrs.NewLegacyProvider("baz"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyFromState, }, }, Children: []*moduledeps.Module{ { Name: "child", Providers: make(moduledeps.Providers), Children: []*moduledeps.Module{ { Name: "grandchild", Providers: moduledeps.Providers{ addrs.NewLegacyProvider("banana"): moduledeps.ProviderDependency{ Constraints: discovery.AllVersions, Reason: moduledeps.ProviderDependencyFromState, }, }, }, }, }, }, }, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { var root *configs.Config if test.ConfigDir != "" { root = testModule(t, test.ConfigDir) } got := ConfigTreeDependencies(root, MustShimLegacyState(test.State)) if !cmp.Equal(got, test.Want) { t.Error(cmp.Diff(got, test.Want)) } }) } }