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  }