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  }