github.com/itscaro/cli@v0.0.0-20190705081621-c9db0fe93829/cli/config/configfile/file_test.go (about) 1 package configfile 2 3 import ( 4 "bytes" 5 "io/ioutil" 6 "os" 7 "testing" 8 9 "github.com/docker/cli/cli/config/credentials" 10 "github.com/docker/cli/cli/config/types" 11 "gotest.tools/assert" 12 is "gotest.tools/assert/cmp" 13 "gotest.tools/golden" 14 ) 15 16 func TestEncodeAuth(t *testing.T) { 17 newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"} 18 authStr := encodeAuth(newAuthConfig) 19 20 expected := &types.AuthConfig{} 21 var err error 22 expected.Username, expected.Password, err = decodeAuth(authStr) 23 assert.NilError(t, err) 24 assert.Check(t, is.DeepEqual(expected, newAuthConfig)) 25 } 26 27 func TestProxyConfig(t *testing.T) { 28 httpProxy := "http://proxy.mycorp.com:3128" 29 httpsProxy := "https://user:password@proxy.mycorp.com:3129" 30 ftpProxy := "http://ftpproxy.mycorp.com:21" 31 noProxy := "*.intra.mycorp.com" 32 defaultProxyConfig := ProxyConfig{ 33 HTTPProxy: httpProxy, 34 HTTPSProxy: httpsProxy, 35 FTPProxy: ftpProxy, 36 NoProxy: noProxy, 37 } 38 39 cfg := ConfigFile{ 40 Proxies: map[string]ProxyConfig{ 41 "default": defaultProxyConfig, 42 }, 43 } 44 45 proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", nil) 46 expected := map[string]*string{ 47 "HTTP_PROXY": &httpProxy, 48 "http_proxy": &httpProxy, 49 "HTTPS_PROXY": &httpsProxy, 50 "https_proxy": &httpsProxy, 51 "FTP_PROXY": &ftpProxy, 52 "ftp_proxy": &ftpProxy, 53 "NO_PROXY": &noProxy, 54 "no_proxy": &noProxy, 55 } 56 assert.Check(t, is.DeepEqual(expected, proxyConfig)) 57 } 58 59 func TestProxyConfigOverride(t *testing.T) { 60 httpProxy := "http://proxy.mycorp.com:3128" 61 overrideHTTPProxy := "http://proxy.example.com:3128" 62 overrideNoProxy := "" 63 httpsProxy := "https://user:password@proxy.mycorp.com:3129" 64 ftpProxy := "http://ftpproxy.mycorp.com:21" 65 noProxy := "*.intra.mycorp.com" 66 defaultProxyConfig := ProxyConfig{ 67 HTTPProxy: httpProxy, 68 HTTPSProxy: httpsProxy, 69 FTPProxy: ftpProxy, 70 NoProxy: noProxy, 71 } 72 73 cfg := ConfigFile{ 74 Proxies: map[string]ProxyConfig{ 75 "default": defaultProxyConfig, 76 }, 77 } 78 79 clone := func(s string) *string { 80 s2 := s 81 return &s2 82 } 83 84 ropts := map[string]*string{ 85 "HTTP_PROXY": clone(overrideHTTPProxy), 86 "NO_PROXY": clone(overrideNoProxy), 87 } 88 proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", ropts) 89 expected := map[string]*string{ 90 "HTTP_PROXY": &overrideHTTPProxy, 91 "http_proxy": &httpProxy, 92 "HTTPS_PROXY": &httpsProxy, 93 "https_proxy": &httpsProxy, 94 "FTP_PROXY": &ftpProxy, 95 "ftp_proxy": &ftpProxy, 96 "NO_PROXY": &overrideNoProxy, 97 "no_proxy": &noProxy, 98 } 99 assert.Check(t, is.DeepEqual(expected, proxyConfig)) 100 } 101 102 func TestProxyConfigPerHost(t *testing.T) { 103 httpProxy := "http://proxy.mycorp.com:3128" 104 httpsProxy := "https://user:password@proxy.mycorp.com:3129" 105 ftpProxy := "http://ftpproxy.mycorp.com:21" 106 noProxy := "*.intra.mycorp.com" 107 108 extHTTPProxy := "http://proxy.example.com:3128" 109 extHTTPSProxy := "https://user:password@proxy.example.com:3129" 110 extFTPProxy := "http://ftpproxy.example.com:21" 111 extNoProxy := "*.intra.example.com" 112 113 defaultProxyConfig := ProxyConfig{ 114 HTTPProxy: httpProxy, 115 HTTPSProxy: httpsProxy, 116 FTPProxy: ftpProxy, 117 NoProxy: noProxy, 118 } 119 externalProxyConfig := ProxyConfig{ 120 HTTPProxy: extHTTPProxy, 121 HTTPSProxy: extHTTPSProxy, 122 FTPProxy: extFTPProxy, 123 NoProxy: extNoProxy, 124 } 125 126 cfg := ConfigFile{ 127 Proxies: map[string]ProxyConfig{ 128 "default": defaultProxyConfig, 129 "tcp://example.docker.com:2376": externalProxyConfig, 130 }, 131 } 132 133 proxyConfig := cfg.ParseProxyConfig("tcp://example.docker.com:2376", nil) 134 expected := map[string]*string{ 135 "HTTP_PROXY": &extHTTPProxy, 136 "http_proxy": &extHTTPProxy, 137 "HTTPS_PROXY": &extHTTPSProxy, 138 "https_proxy": &extHTTPSProxy, 139 "FTP_PROXY": &extFTPProxy, 140 "ftp_proxy": &extFTPProxy, 141 "NO_PROXY": &extNoProxy, 142 "no_proxy": &extNoProxy, 143 } 144 assert.Check(t, is.DeepEqual(expected, proxyConfig)) 145 } 146 147 func TestConfigFile(t *testing.T) { 148 configFilename := "configFilename" 149 configFile := New(configFilename) 150 151 assert.Check(t, is.Equal(configFilename, configFile.Filename)) 152 } 153 154 type mockNativeStore struct { 155 GetAllCallCount int 156 authConfigs map[string]types.AuthConfig 157 } 158 159 func (c *mockNativeStore) Erase(registryHostname string) error { 160 delete(c.authConfigs, registryHostname) 161 return nil 162 } 163 164 func (c *mockNativeStore) Get(registryHostname string) (types.AuthConfig, error) { 165 return c.authConfigs[registryHostname], nil 166 } 167 168 func (c *mockNativeStore) GetAll() (map[string]types.AuthConfig, error) { 169 c.GetAllCallCount = c.GetAllCallCount + 1 170 return c.authConfigs, nil 171 } 172 173 func (c *mockNativeStore) Store(authConfig types.AuthConfig) error { 174 return nil 175 } 176 177 // make sure it satisfies the interface 178 var _ credentials.Store = (*mockNativeStore)(nil) 179 180 func NewMockNativeStore(authConfigs map[string]types.AuthConfig) credentials.Store { 181 return &mockNativeStore{authConfigs: authConfigs} 182 } 183 184 func TestGetAllCredentialsFileStoreOnly(t *testing.T) { 185 configFile := New("filename") 186 exampleAuth := types.AuthConfig{ 187 Username: "user", 188 Password: "pass", 189 } 190 configFile.AuthConfigs["example.com/foo"] = exampleAuth 191 192 authConfigs, err := configFile.GetAllCredentials() 193 assert.NilError(t, err) 194 195 expected := make(map[string]types.AuthConfig) 196 expected["example.com/foo"] = exampleAuth 197 assert.Check(t, is.DeepEqual(expected, authConfigs)) 198 } 199 200 func TestGetAllCredentialsCredsStore(t *testing.T) { 201 configFile := New("filename") 202 configFile.CredentialsStore = "test_creds_store" 203 testRegistryHostname := "example.com" 204 expectedAuth := types.AuthConfig{ 205 Username: "user", 206 Password: "pass", 207 } 208 209 testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth}) 210 211 tmpNewNativeStore := newNativeStore 212 defer func() { newNativeStore = tmpNewNativeStore }() 213 newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { 214 return testCredsStore 215 } 216 217 authConfigs, err := configFile.GetAllCredentials() 218 assert.NilError(t, err) 219 220 expected := make(map[string]types.AuthConfig) 221 expected[testRegistryHostname] = expectedAuth 222 assert.Check(t, is.DeepEqual(expected, authConfigs)) 223 assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount)) 224 } 225 226 func TestGetAllCredentialsCredHelper(t *testing.T) { 227 testCredHelperSuffix := "test_cred_helper" 228 testCredHelperRegistryHostname := "credhelper.com" 229 testExtraCredHelperRegistryHostname := "somethingweird.com" 230 231 unexpectedCredHelperAuth := types.AuthConfig{ 232 Username: "file_store_user", 233 Password: "file_store_pass", 234 } 235 expectedCredHelperAuth := types.AuthConfig{ 236 Username: "cred_helper_user", 237 Password: "cred_helper_pass", 238 } 239 240 configFile := New("filename") 241 configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix} 242 243 testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{ 244 testCredHelperRegistryHostname: expectedCredHelperAuth, 245 // Add in an extra auth entry which doesn't appear in CredentialHelpers section of the configFile. 246 // This verifies that only explicitly configured registries are being requested from the cred helpers. 247 testExtraCredHelperRegistryHostname: unexpectedCredHelperAuth, 248 }) 249 250 tmpNewNativeStore := newNativeStore 251 defer func() { newNativeStore = tmpNewNativeStore }() 252 newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { 253 return testCredHelper 254 } 255 256 authConfigs, err := configFile.GetAllCredentials() 257 assert.NilError(t, err) 258 259 expected := make(map[string]types.AuthConfig) 260 expected[testCredHelperRegistryHostname] = expectedCredHelperAuth 261 assert.Check(t, is.DeepEqual(expected, authConfigs)) 262 assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) 263 } 264 265 func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) { 266 testFileStoreRegistryHostname := "example.com" 267 testCredHelperSuffix := "test_cred_helper" 268 testCredHelperRegistryHostname := "credhelper.com" 269 270 expectedFileStoreAuth := types.AuthConfig{ 271 Username: "file_store_user", 272 Password: "file_store_pass", 273 } 274 expectedCredHelperAuth := types.AuthConfig{ 275 Username: "cred_helper_user", 276 Password: "cred_helper_pass", 277 } 278 279 configFile := New("filename") 280 configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix} 281 configFile.AuthConfigs[testFileStoreRegistryHostname] = expectedFileStoreAuth 282 283 testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}) 284 285 newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { 286 return testCredHelper 287 } 288 289 tmpNewNativeStore := newNativeStore 290 defer func() { newNativeStore = tmpNewNativeStore }() 291 authConfigs, err := configFile.GetAllCredentials() 292 assert.NilError(t, err) 293 294 expected := make(map[string]types.AuthConfig) 295 expected[testFileStoreRegistryHostname] = expectedFileStoreAuth 296 expected[testCredHelperRegistryHostname] = expectedCredHelperAuth 297 assert.Check(t, is.DeepEqual(expected, authConfigs)) 298 assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) 299 } 300 301 func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) { 302 testCredStoreSuffix := "test_creds_store" 303 testCredStoreRegistryHostname := "credstore.com" 304 testCredHelperSuffix := "test_cred_helper" 305 testCredHelperRegistryHostname := "credhelper.com" 306 307 configFile := New("filename") 308 configFile.CredentialsStore = testCredStoreSuffix 309 configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix} 310 311 expectedCredStoreAuth := types.AuthConfig{ 312 Username: "cred_store_user", 313 Password: "cred_store_pass", 314 } 315 expectedCredHelperAuth := types.AuthConfig{ 316 Username: "cred_helper_user", 317 Password: "cred_helper_pass", 318 } 319 320 testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}) 321 testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth}) 322 323 tmpNewNativeStore := newNativeStore 324 defer func() { newNativeStore = tmpNewNativeStore }() 325 newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { 326 if helperSuffix == testCredHelperSuffix { 327 return testCredHelper 328 } 329 return testCredsStore 330 } 331 332 authConfigs, err := configFile.GetAllCredentials() 333 assert.NilError(t, err) 334 335 expected := make(map[string]types.AuthConfig) 336 expected[testCredStoreRegistryHostname] = expectedCredStoreAuth 337 expected[testCredHelperRegistryHostname] = expectedCredHelperAuth 338 assert.Check(t, is.DeepEqual(expected, authConfigs)) 339 assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount)) 340 assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) 341 } 342 343 func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) { 344 testCredStoreSuffix := "test_creds_store" 345 testCredHelperSuffix := "test_cred_helper" 346 testRegistryHostname := "example.com" 347 348 configFile := New("filename") 349 configFile.CredentialsStore = testCredStoreSuffix 350 configFile.CredentialHelpers = map[string]string{testRegistryHostname: testCredHelperSuffix} 351 352 unexpectedCredStoreAuth := types.AuthConfig{ 353 Username: "cred_store_user", 354 Password: "cred_store_pass", 355 } 356 expectedCredHelperAuth := types.AuthConfig{ 357 Username: "cred_helper_user", 358 Password: "cred_helper_pass", 359 } 360 361 testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth}) 362 testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth}) 363 364 tmpNewNativeStore := newNativeStore 365 defer func() { newNativeStore = tmpNewNativeStore }() 366 newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store { 367 if helperSuffix == testCredHelperSuffix { 368 return testCredHelper 369 } 370 return testCredsStore 371 } 372 373 authConfigs, err := configFile.GetAllCredentials() 374 assert.NilError(t, err) 375 376 expected := make(map[string]types.AuthConfig) 377 expected[testRegistryHostname] = expectedCredHelperAuth 378 assert.Check(t, is.DeepEqual(expected, authConfigs)) 379 assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount)) 380 assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount)) 381 } 382 383 func TestCheckKubernetesConfigurationRaiseAnErrorOnInvalidValue(t *testing.T) { 384 testCases := []struct { 385 name string 386 config *KubernetesConfig 387 expectError bool 388 }{ 389 { 390 "no kubernetes config is valid", 391 nil, 392 false, 393 }, 394 { 395 "enabled is valid", 396 &KubernetesConfig{AllNamespaces: "enabled"}, 397 false, 398 }, 399 { 400 "disabled is valid", 401 &KubernetesConfig{AllNamespaces: "disabled"}, 402 false, 403 }, 404 { 405 "empty string is valid", 406 &KubernetesConfig{AllNamespaces: ""}, 407 false, 408 }, 409 { 410 "other value is invalid", 411 &KubernetesConfig{AllNamespaces: "unknown"}, 412 true, 413 }, 414 } 415 for _, test := range testCases { 416 err := checkKubernetesConfiguration(test.config) 417 if test.expectError { 418 assert.Assert(t, err != nil, test.name) 419 } else { 420 assert.NilError(t, err, test.name) 421 } 422 } 423 } 424 425 func TestSave(t *testing.T) { 426 configFile := New("test-save") 427 defer os.Remove("test-save") 428 err := configFile.Save() 429 assert.NilError(t, err) 430 cfg, err := ioutil.ReadFile("test-save") 431 assert.NilError(t, err) 432 assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}")) 433 } 434 435 func TestPluginConfig(t *testing.T) { 436 configFile := New("test-plugin") 437 defer os.Remove("test-plugin") 438 439 // Populate some initial values 440 configFile.SetPluginConfig("plugin1", "data1", "some string") 441 configFile.SetPluginConfig("plugin1", "data2", "42") 442 configFile.SetPluginConfig("plugin2", "data3", "some other string") 443 444 // Save a config file with some plugin config 445 err := configFile.Save() 446 assert.NilError(t, err) 447 448 // Read it back and check it has the expected content 449 cfg, err := ioutil.ReadFile("test-plugin") 450 assert.NilError(t, err) 451 golden.Assert(t, string(cfg), "plugin-config.golden") 452 453 // Load it, resave and check again that the content is 454 // preserved through a load/save cycle. 455 configFile = New("test-plugin2") 456 defer os.Remove("test-plugin2") 457 assert.NilError(t, configFile.LoadFromReader(bytes.NewReader(cfg))) 458 err = configFile.Save() 459 assert.NilError(t, err) 460 cfg, err = ioutil.ReadFile("test-plugin2") 461 assert.NilError(t, err) 462 golden.Assert(t, string(cfg), "plugin-config.golden") 463 464 // Check that the contents was reloaded properly 465 v, ok := configFile.PluginConfig("plugin1", "data1") 466 assert.Assert(t, ok) 467 assert.Equal(t, v, "some string") 468 v, ok = configFile.PluginConfig("plugin1", "data2") 469 assert.Assert(t, ok) 470 assert.Equal(t, v, "42") 471 v, ok = configFile.PluginConfig("plugin1", "data3") 472 assert.Assert(t, !ok) 473 assert.Equal(t, v, "") 474 v, ok = configFile.PluginConfig("plugin2", "data3") 475 assert.Assert(t, ok) 476 assert.Equal(t, v, "some other string") 477 v, ok = configFile.PluginConfig("plugin2", "data4") 478 assert.Assert(t, !ok) 479 assert.Equal(t, v, "") 480 v, ok = configFile.PluginConfig("plugin3", "data5") 481 assert.Assert(t, !ok) 482 assert.Equal(t, v, "") 483 484 // Add, remove and modify 485 configFile.SetPluginConfig("plugin1", "data1", "some replacement string") // replacing a key 486 configFile.SetPluginConfig("plugin1", "data2", "") // deleting a key 487 configFile.SetPluginConfig("plugin1", "data3", "some additional string") // new key 488 configFile.SetPluginConfig("plugin2", "data3", "") // delete the whole plugin, since this was the only data 489 configFile.SetPluginConfig("plugin3", "data5", "a new plugin") // add a new plugin 490 491 err = configFile.Save() 492 assert.NilError(t, err) 493 494 // Read it back and check it has the expected content again 495 cfg, err = ioutil.ReadFile("test-plugin2") 496 assert.NilError(t, err) 497 golden.Assert(t, string(cfg), "plugin-config-2.golden") 498 }