2016-12-25 14:31:52 -05:00
|
|
|
package configfile
|
|
|
|
|
|
|
|
import (
|
2019-01-31 10:32:25 -05:00
|
|
|
"bytes"
|
2019-10-02 15:41:50 -04:00
|
|
|
"encoding/json"
|
2023-01-30 12:48:01 -05:00
|
|
|
"errors"
|
2018-09-10 16:39:30 -04:00
|
|
|
"os"
|
2016-12-25 14:31:52 -05:00
|
|
|
"testing"
|
|
|
|
|
2018-01-26 17:38:04 -05:00
|
|
|
"github.com/docker/cli/cli/config/credentials"
|
2017-10-15 15:39:56 -04:00
|
|
|
"github.com/docker/cli/cli/config/types"
|
2020-02-22 12:12:14 -05:00
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
is "gotest.tools/v3/assert/cmp"
|
2020-07-15 11:59:47 -04:00
|
|
|
"gotest.tools/v3/fs"
|
2020-02-22 12:12:14 -05:00
|
|
|
"gotest.tools/v3/golden"
|
2016-12-25 14:31:52 -05:00
|
|
|
)
|
|
|
|
|
|
|
|
func TestEncodeAuth(t *testing.T) {
|
|
|
|
newAuthConfig := &types.AuthConfig{Username: "ken", Password: "test"}
|
|
|
|
authStr := encodeAuth(newAuthConfig)
|
2017-06-22 12:03:58 -04:00
|
|
|
|
|
|
|
expected := &types.AuthConfig{}
|
2016-12-25 14:31:52 -05:00
|
|
|
var err error
|
2017-06-22 12:03:58 -04:00
|
|
|
expected.Username, expected.Password, err = decodeAuth(authStr)
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.DeepEqual(expected, newAuthConfig))
|
2016-12-25 14:31:52 -05:00
|
|
|
}
|
2017-01-31 00:15:51 -05:00
|
|
|
|
|
|
|
func TestProxyConfig(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
var (
|
|
|
|
httpProxy = "http://proxy.mycorp.example.com:3128"
|
|
|
|
httpsProxy = "https://user:password@proxy.mycorp.example.com:3129"
|
|
|
|
ftpProxy = "http://ftpproxy.mycorp.example.com:21"
|
|
|
|
noProxy = "*.intra.mycorp.example.com"
|
2021-04-30 05:53:53 -04:00
|
|
|
allProxy = "socks://example.com:1234"
|
2021-04-30 03:43:13 -04:00
|
|
|
|
|
|
|
defaultProxyConfig = ProxyConfig{
|
|
|
|
HTTPProxy: httpProxy,
|
|
|
|
HTTPSProxy: httpsProxy,
|
|
|
|
FTPProxy: ftpProxy,
|
|
|
|
NoProxy: noProxy,
|
2021-04-30 05:53:53 -04:00
|
|
|
AllProxy: allProxy,
|
2021-04-30 03:43:13 -04:00
|
|
|
}
|
|
|
|
)
|
2017-01-31 00:15:51 -05:00
|
|
|
|
|
|
|
cfg := ConfigFile{
|
|
|
|
Proxies: map[string]ProxyConfig{
|
|
|
|
"default": defaultProxyConfig,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-10-15 15:39:56 -04:00
|
|
|
proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", nil)
|
2017-01-31 00:15:51 -05:00
|
|
|
expected := map[string]*string{
|
|
|
|
"HTTP_PROXY": &httpProxy,
|
|
|
|
"http_proxy": &httpProxy,
|
|
|
|
"HTTPS_PROXY": &httpsProxy,
|
|
|
|
"https_proxy": &httpsProxy,
|
|
|
|
"FTP_PROXY": &ftpProxy,
|
|
|
|
"ftp_proxy": &ftpProxy,
|
|
|
|
"NO_PROXY": &noProxy,
|
|
|
|
"no_proxy": &noProxy,
|
2021-04-30 05:53:53 -04:00
|
|
|
"ALL_PROXY": &allProxy,
|
|
|
|
"all_proxy": &allProxy,
|
2017-01-31 00:15:51 -05:00
|
|
|
}
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, proxyConfig))
|
2017-01-31 00:15:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestProxyConfigOverride(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
var (
|
|
|
|
httpProxy = "http://proxy.mycorp.example.com:3128"
|
|
|
|
httpProxyOverride = "http://proxy.example.com:3128"
|
|
|
|
httpsProxy = "https://user:password@proxy.mycorp.example.com:3129"
|
|
|
|
ftpProxy = "http://ftpproxy.mycorp.example.com:21"
|
|
|
|
noProxy = "*.intra.mycorp.example.com"
|
|
|
|
noProxyOverride = ""
|
|
|
|
|
|
|
|
defaultProxyConfig = ProxyConfig{
|
|
|
|
HTTPProxy: httpProxy,
|
|
|
|
HTTPSProxy: httpsProxy,
|
|
|
|
FTPProxy: ftpProxy,
|
|
|
|
NoProxy: noProxy,
|
|
|
|
}
|
|
|
|
)
|
2017-01-31 00:15:51 -05:00
|
|
|
|
|
|
|
cfg := ConfigFile{
|
|
|
|
Proxies: map[string]ProxyConfig{
|
|
|
|
"default": defaultProxyConfig,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-10-15 15:39:56 -04:00
|
|
|
clone := func(s string) *string {
|
|
|
|
s2 := s
|
|
|
|
return &s2
|
|
|
|
}
|
|
|
|
|
|
|
|
ropts := map[string]*string{
|
2021-04-30 03:43:13 -04:00
|
|
|
"HTTP_PROXY": clone(httpProxyOverride),
|
|
|
|
"NO_PROXY": clone(noProxyOverride),
|
2017-01-31 00:15:51 -05:00
|
|
|
}
|
|
|
|
proxyConfig := cfg.ParseProxyConfig("/var/run/docker.sock", ropts)
|
|
|
|
expected := map[string]*string{
|
2021-04-30 03:43:13 -04:00
|
|
|
"HTTP_PROXY": &httpProxyOverride,
|
2017-01-31 00:15:51 -05:00
|
|
|
"http_proxy": &httpProxy,
|
|
|
|
"HTTPS_PROXY": &httpsProxy,
|
|
|
|
"https_proxy": &httpsProxy,
|
|
|
|
"FTP_PROXY": &ftpProxy,
|
|
|
|
"ftp_proxy": &ftpProxy,
|
2021-04-30 03:43:13 -04:00
|
|
|
"NO_PROXY": &noProxyOverride,
|
2017-01-31 00:15:51 -05:00
|
|
|
"no_proxy": &noProxy,
|
|
|
|
}
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, proxyConfig))
|
2017-01-31 00:15:51 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestProxyConfigPerHost(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
var (
|
|
|
|
httpProxy = "http://proxy.mycorp.example.com:3128"
|
|
|
|
httpsProxy = "https://user:password@proxy.mycorp.example.com:3129"
|
|
|
|
ftpProxy = "http://ftpproxy.mycorp.example.com:21"
|
|
|
|
noProxy = "*.intra.mycorp.example.com"
|
|
|
|
|
|
|
|
extHTTPProxy = "http://proxy.example.com:3128"
|
|
|
|
extHTTPSProxy = "https://user:password@proxy.example.com:3129"
|
|
|
|
extFTPProxy = "http://ftpproxy.example.com:21"
|
|
|
|
extNoProxy = "*.intra.example.com"
|
|
|
|
|
|
|
|
defaultProxyConfig = ProxyConfig{
|
|
|
|
HTTPProxy: httpProxy,
|
|
|
|
HTTPSProxy: httpsProxy,
|
|
|
|
FTPProxy: ftpProxy,
|
|
|
|
NoProxy: noProxy,
|
|
|
|
}
|
|
|
|
|
|
|
|
externalProxyConfig = ProxyConfig{
|
|
|
|
HTTPProxy: extHTTPProxy,
|
|
|
|
HTTPSProxy: extHTTPSProxy,
|
|
|
|
FTPProxy: extFTPProxy,
|
|
|
|
NoProxy: extNoProxy,
|
|
|
|
}
|
|
|
|
)
|
2017-01-31 00:15:51 -05:00
|
|
|
|
|
|
|
cfg := ConfigFile{
|
|
|
|
Proxies: map[string]ProxyConfig{
|
|
|
|
"default": defaultProxyConfig,
|
|
|
|
"tcp://example.docker.com:2376": externalProxyConfig,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2017-10-15 15:39:56 -04:00
|
|
|
proxyConfig := cfg.ParseProxyConfig("tcp://example.docker.com:2376", nil)
|
2017-01-31 00:15:51 -05:00
|
|
|
expected := map[string]*string{
|
|
|
|
"HTTP_PROXY": &extHTTPProxy,
|
|
|
|
"http_proxy": &extHTTPProxy,
|
|
|
|
"HTTPS_PROXY": &extHTTPSProxy,
|
|
|
|
"https_proxy": &extHTTPSProxy,
|
|
|
|
"FTP_PROXY": &extFTPProxy,
|
|
|
|
"ftp_proxy": &extFTPProxy,
|
|
|
|
"NO_PROXY": &extNoProxy,
|
|
|
|
"no_proxy": &extNoProxy,
|
|
|
|
}
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, proxyConfig))
|
2017-01-31 00:15:51 -05:00
|
|
|
}
|
2017-06-12 14:36:49 -04:00
|
|
|
|
|
|
|
func TestConfigFile(t *testing.T) {
|
|
|
|
configFilename := "configFilename"
|
2017-06-27 10:31:38 -04:00
|
|
|
configFile := New(configFilename)
|
2017-06-12 14:36:49 -04:00
|
|
|
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.Equal(configFilename, configFile.Filename))
|
2017-06-12 14:36:49 -04:00
|
|
|
}
|
2017-06-22 12:03:58 -04:00
|
|
|
|
2018-01-26 17:38:04 -05:00
|
|
|
type mockNativeStore struct {
|
2023-01-30 12:48:01 -05:00
|
|
|
GetAllCallCount int
|
|
|
|
authConfigs map[string]types.AuthConfig
|
|
|
|
authConfigErrors map[string]error
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockNativeStore) Erase(registryHostname string) error {
|
|
|
|
delete(c.authConfigs, registryHostname)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockNativeStore) Get(registryHostname string) (types.AuthConfig, error) {
|
2023-01-30 12:48:01 -05:00
|
|
|
return c.authConfigs[registryHostname], c.authConfigErrors[registryHostname]
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *mockNativeStore) GetAll() (map[string]types.AuthConfig, error) {
|
linting: address assorted issues found by gocritic
internal/test/builders/config.go:36:15: captLocal: `ID' should not be capitalized (gocritic)
func ConfigID(ID string) func(config *swarm.Config) {
^
internal/test/builders/secret.go:45:15: captLocal: `ID' should not be capitalized (gocritic)
func SecretID(ID string) func(secret *swarm.Secret) {
^
internal/test/builders/service.go:21:16: captLocal: `ID' should not be capitalized (gocritic)
func ServiceID(ID string) func(*swarm.Service) {
^
cli/command/image/formatter_history.go:100:15: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(c.h.CreatedBy, "\t", " ", -1)` (gocritic)
createdBy := strings.Replace(c.h.CreatedBy, "\t", " ", -1)
^
e2e/image/push_test.go:246:34: badCall: suspicious Join on 1 argument (gocritic)
assert.NilError(t, os.RemoveAll(filepath.Join(dir.Join("trust"))))
^
e2e/image/push_test.go:313:34: badCall: suspicious Join on 1 argument (gocritic)
assert.NilError(t, os.RemoveAll(filepath.Join(dir.Join("trust"))))
^
cli/config/configfile/file_test.go:185:2: assignOp: replace `c.GetAllCallCount = c.GetAllCallCount + 1` with `c.GetAllCallCount++` (gocritic)
c.GetAllCallCount = c.GetAllCallCount + 1
^
cli/command/context/inspect_test.go:20:58: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(si.MetadataPath, `\`, `\\`, -1)` (gocritic)
expected = strings.Replace(expected, "<METADATA_PATH>", strings.Replace(si.MetadataPath, `\`, `\\`, -1), 1)
^
cli/command/context/inspect_test.go:21:53: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(si.TLSPath, `\`, `\\`, -1)` (gocritic)
expected = strings.Replace(expected, "<TLS_PATH>", strings.Replace(si.TLSPath, `\`, `\\`, -1), 1)
^
cli/command/container/formatter_stats.go:119:46: captLocal: `Stats' should not be capitalized (gocritic)
func statsFormatWrite(ctx formatter.Context, Stats []StatsEntry, osType string, trunc bool) error {
^
cli/command/container/stats_helpers.go:209:4: assignOp: replace `blkRead = blkRead + bioEntry.Value` with `blkRead += bioEntry.Value` (gocritic)
blkRead = blkRead + bioEntry.Value
^
cli/command/container/stats_helpers.go:211:4: assignOp: replace `blkWrite = blkWrite + bioEntry.Value` with `blkWrite += bioEntry.Value` (gocritic)
blkWrite = blkWrite + bioEntry.Value
^
cli/command/registry/formatter_search.go:67:10: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(c.s.Description, "\n", " ", -1)` (gocritic)
desc := strings.Replace(c.s.Description, "\n", " ", -1)
^
cli/command/registry/formatter_search.go:68:9: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(desc, "\r", " ", -1)` (gocritic)
desc = strings.Replace(desc, "\r", " ", -1)
^
cli/command/service/list_test.go:164:5: assignOp: replace `tc.doc = tc.doc + " with quiet"` with `tc.doc += " with quiet"` (gocritic)
tc.doc = tc.doc + " with quiet"
^
cli/command/service/progress/progress.go:274:11: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(errMsg, "\n", " ", -1)` (gocritic)
errMsg = strings.Replace(errMsg, "\n", " ", -1)
^
cli/manifest/store/store.go:153:9: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(fileName, "/", "_", -1)` (gocritic)
return strings.Replace(fileName, "/", "_", -1)
^
cli/manifest/store/store.go:152:14: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(ref, ":", "-", -1)` (gocritic)
fileName := strings.Replace(ref, ":", "-", -1)
^
cli/command/plugin/formatter.go:79:10: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(c.p.Config.Description, "\n", "", -1)` (gocritic)
desc := strings.Replace(c.p.Config.Description, "\n", "", -1)
^
cli/command/plugin/formatter.go:80:9: wrapperFunc: use strings.ReplaceAll method in `strings.Replace(desc, "\r", "", -1)` (gocritic)
desc = strings.Replace(desc, "\r", "", -1)
^
cli/compose/convert/service.go:642:23: captLocal: `DNS' should not be capitalized (gocritic)
func convertDNSConfig(DNS []string, DNSSearch []string) *swarm.DNSConfig {
^
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2023-11-20 07:30:05 -05:00
|
|
|
c.GetAllCallCount++
|
2018-01-26 17:38:04 -05:00
|
|
|
return c.authConfigs, nil
|
|
|
|
}
|
|
|
|
|
2023-03-30 10:44:02 -04:00
|
|
|
func (c *mockNativeStore) Store(_ types.AuthConfig) error {
|
2018-01-26 17:38:04 -05:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// make sure it satisfies the interface
|
|
|
|
var _ credentials.Store = (*mockNativeStore)(nil)
|
|
|
|
|
2023-01-30 12:48:01 -05:00
|
|
|
func NewMockNativeStore(authConfigs map[string]types.AuthConfig, authConfigErrors map[string]error) credentials.Store {
|
|
|
|
return &mockNativeStore{authConfigs: authConfigs, authConfigErrors: authConfigErrors}
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetAllCredentialsFileStoreOnly(t *testing.T) {
|
2017-06-27 10:31:38 -04:00
|
|
|
configFile := New("filename")
|
2017-06-22 12:03:58 -04:00
|
|
|
exampleAuth := types.AuthConfig{
|
|
|
|
Username: "user",
|
|
|
|
Password: "pass",
|
|
|
|
}
|
|
|
|
configFile.AuthConfigs["example.com/foo"] = exampleAuth
|
|
|
|
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
2017-06-22 12:03:58 -04:00
|
|
|
|
|
|
|
expected := make(map[string]types.AuthConfig)
|
|
|
|
expected["example.com/foo"] = exampleAuth
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, authConfigs))
|
2017-06-22 12:03:58 -04:00
|
|
|
}
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
func TestGetAllCredentialsCredsStore(t *testing.T) {
|
|
|
|
configFile := New("filename")
|
|
|
|
configFile.CredentialsStore = "test_creds_store"
|
|
|
|
testRegistryHostname := "example.com"
|
|
|
|
expectedAuth := types.AuthConfig{
|
|
|
|
Username: "user",
|
|
|
|
Password: "pass",
|
|
|
|
}
|
|
|
|
|
2023-01-30 12:48:01 -05:00
|
|
|
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedAuth}, nil)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
tmpNewNativeStore := newNativeStore
|
|
|
|
defer func() { newNativeStore = tmpNewNativeStore }()
|
|
|
|
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
|
|
|
return testCredsStore
|
|
|
|
}
|
|
|
|
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
expected := make(map[string]types.AuthConfig)
|
|
|
|
expected[testRegistryHostname] = expectedAuth
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, authConfigs))
|
|
|
|
assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount))
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
2023-01-30 12:48:01 -05:00
|
|
|
func TestGetAllCredentialsCredStoreErrorHandling(t *testing.T) {
|
|
|
|
const (
|
|
|
|
workingHelperRegistryHostname = "working-helper.example.com"
|
|
|
|
brokenHelperRegistryHostname = "broken-helper.example.com"
|
|
|
|
)
|
|
|
|
configFile := New("filename")
|
|
|
|
configFile.CredentialHelpers = map[string]string{
|
|
|
|
workingHelperRegistryHostname: "cred_helper",
|
|
|
|
brokenHelperRegistryHostname: "broken_cred_helper",
|
|
|
|
}
|
|
|
|
expectedAuth := types.AuthConfig{
|
|
|
|
Username: "username",
|
|
|
|
Password: "pass",
|
|
|
|
}
|
|
|
|
// configure the mock store to throw an error
|
|
|
|
// when calling the helper for this registry
|
|
|
|
authErrors := map[string]error{
|
|
|
|
brokenHelperRegistryHostname: errors.New("an error"),
|
|
|
|
}
|
|
|
|
|
|
|
|
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{
|
|
|
|
workingHelperRegistryHostname: expectedAuth,
|
|
|
|
// configure an auth entry for the "broken" credential
|
|
|
|
// helper that will throw an error
|
|
|
|
brokenHelperRegistryHostname: {},
|
|
|
|
}, authErrors)
|
|
|
|
|
|
|
|
tmpNewNativeStore := newNativeStore
|
|
|
|
defer func() { newNativeStore = tmpNewNativeStore }()
|
|
|
|
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
|
|
|
return testCredsStore
|
|
|
|
}
|
|
|
|
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
|
|
|
|
|
|
|
// make sure we're still returning the expected credentials
|
|
|
|
// and skipping the ones throwing an error
|
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(1, len(authConfigs)))
|
|
|
|
assert.Check(t, is.DeepEqual(expectedAuth, authConfigs[workingHelperRegistryHostname]))
|
|
|
|
}
|
|
|
|
|
2018-01-26 17:38:04 -05:00
|
|
|
func TestGetAllCredentialsCredHelper(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
const (
|
|
|
|
testCredHelperSuffix = "test_cred_helper"
|
|
|
|
testCredHelperRegistryHostname = "credhelper.com"
|
|
|
|
testExtraCredHelperRegistryHostname = "somethingweird.com"
|
|
|
|
)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
unexpectedCredHelperAuth := types.AuthConfig{
|
|
|
|
Username: "file_store_user",
|
|
|
|
Password: "file_store_pass",
|
|
|
|
}
|
|
|
|
expectedCredHelperAuth := types.AuthConfig{
|
|
|
|
Username: "cred_helper_user",
|
|
|
|
Password: "cred_helper_pass",
|
|
|
|
}
|
|
|
|
|
|
|
|
configFile := New("filename")
|
|
|
|
configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix}
|
|
|
|
|
|
|
|
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{
|
|
|
|
testCredHelperRegistryHostname: expectedCredHelperAuth,
|
|
|
|
// Add in an extra auth entry which doesn't appear in CredentialHelpers section of the configFile.
|
|
|
|
// This verifies that only explicitly configured registries are being requested from the cred helpers.
|
|
|
|
testExtraCredHelperRegistryHostname: unexpectedCredHelperAuth,
|
2023-01-30 12:48:01 -05:00
|
|
|
}, nil)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
tmpNewNativeStore := newNativeStore
|
|
|
|
defer func() { newNativeStore = tmpNewNativeStore }()
|
|
|
|
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
|
|
|
return testCredHelper
|
|
|
|
}
|
|
|
|
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
expected := make(map[string]types.AuthConfig)
|
|
|
|
expected[testCredHelperRegistryHostname] = expectedCredHelperAuth
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, authConfigs))
|
|
|
|
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetAllCredentialsFileStoreAndCredHelper(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
const (
|
|
|
|
testFileStoreRegistryHostname = "example.com"
|
|
|
|
testCredHelperSuffix = "test_cred_helper"
|
|
|
|
testCredHelperRegistryHostname = "credhelper.com"
|
|
|
|
)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
expectedFileStoreAuth := types.AuthConfig{
|
|
|
|
Username: "file_store_user",
|
|
|
|
Password: "file_store_pass",
|
|
|
|
}
|
|
|
|
expectedCredHelperAuth := types.AuthConfig{
|
|
|
|
Username: "cred_helper_user",
|
|
|
|
Password: "cred_helper_pass",
|
|
|
|
}
|
|
|
|
|
|
|
|
configFile := New("filename")
|
|
|
|
configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix}
|
|
|
|
configFile.AuthConfigs[testFileStoreRegistryHostname] = expectedFileStoreAuth
|
|
|
|
|
2023-01-30 12:48:01 -05:00
|
|
|
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}, nil)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
|
|
|
return testCredHelper
|
|
|
|
}
|
|
|
|
|
|
|
|
tmpNewNativeStore := newNativeStore
|
|
|
|
defer func() { newNativeStore = tmpNewNativeStore }()
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
expected := make(map[string]types.AuthConfig)
|
|
|
|
expected[testFileStoreRegistryHostname] = expectedFileStoreAuth
|
|
|
|
expected[testCredHelperRegistryHostname] = expectedCredHelperAuth
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, authConfigs))
|
|
|
|
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetAllCredentialsCredStoreAndCredHelper(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
const (
|
|
|
|
testCredStoreSuffix = "test_creds_store"
|
|
|
|
testCredStoreRegistryHostname = "credstore.com"
|
|
|
|
testCredHelperSuffix = "test_cred_helper"
|
|
|
|
testCredHelperRegistryHostname = "credhelper.com"
|
|
|
|
)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
configFile := New("filename")
|
|
|
|
configFile.CredentialsStore = testCredStoreSuffix
|
|
|
|
configFile.CredentialHelpers = map[string]string{testCredHelperRegistryHostname: testCredHelperSuffix}
|
|
|
|
|
|
|
|
expectedCredStoreAuth := types.AuthConfig{
|
|
|
|
Username: "cred_store_user",
|
|
|
|
Password: "cred_store_pass",
|
|
|
|
}
|
|
|
|
expectedCredHelperAuth := types.AuthConfig{
|
|
|
|
Username: "cred_helper_user",
|
|
|
|
Password: "cred_helper_pass",
|
|
|
|
}
|
|
|
|
|
2023-01-30 12:48:01 -05:00
|
|
|
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testCredHelperRegistryHostname: expectedCredHelperAuth}, nil)
|
|
|
|
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testCredStoreRegistryHostname: expectedCredStoreAuth}, nil)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
tmpNewNativeStore := newNativeStore
|
|
|
|
defer func() { newNativeStore = tmpNewNativeStore }()
|
|
|
|
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
|
|
|
if helperSuffix == testCredHelperSuffix {
|
|
|
|
return testCredHelper
|
|
|
|
}
|
|
|
|
return testCredsStore
|
|
|
|
}
|
|
|
|
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
expected := make(map[string]types.AuthConfig)
|
|
|
|
expected[testCredStoreRegistryHostname] = expectedCredStoreAuth
|
|
|
|
expected[testCredHelperRegistryHostname] = expectedCredHelperAuth
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, authConfigs))
|
|
|
|
assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount))
|
|
|
|
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestGetAllCredentialsCredHelperOverridesDefaultStore(t *testing.T) {
|
2021-04-30 03:43:13 -04:00
|
|
|
const (
|
|
|
|
testCredStoreSuffix = "test_creds_store"
|
|
|
|
testCredHelperSuffix = "test_cred_helper"
|
|
|
|
testRegistryHostname = "example.com"
|
|
|
|
)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
configFile := New("filename")
|
|
|
|
configFile.CredentialsStore = testCredStoreSuffix
|
|
|
|
configFile.CredentialHelpers = map[string]string{testRegistryHostname: testCredHelperSuffix}
|
|
|
|
|
|
|
|
unexpectedCredStoreAuth := types.AuthConfig{
|
|
|
|
Username: "cred_store_user",
|
|
|
|
Password: "cred_store_pass",
|
|
|
|
}
|
|
|
|
expectedCredHelperAuth := types.AuthConfig{
|
|
|
|
Username: "cred_helper_user",
|
|
|
|
Password: "cred_helper_pass",
|
|
|
|
}
|
|
|
|
|
2023-01-30 12:48:01 -05:00
|
|
|
testCredHelper := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: expectedCredHelperAuth}, nil)
|
|
|
|
testCredsStore := NewMockNativeStore(map[string]types.AuthConfig{testRegistryHostname: unexpectedCredStoreAuth}, nil)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
tmpNewNativeStore := newNativeStore
|
|
|
|
defer func() { newNativeStore = tmpNewNativeStore }()
|
|
|
|
newNativeStore = func(configFile *ConfigFile, helperSuffix string) credentials.Store {
|
|
|
|
if helperSuffix == testCredHelperSuffix {
|
|
|
|
return testCredHelper
|
|
|
|
}
|
|
|
|
return testCredsStore
|
|
|
|
}
|
|
|
|
|
|
|
|
authConfigs, err := configFile.GetAllCredentials()
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.NilError(t, err)
|
2018-01-26 17:38:04 -05:00
|
|
|
|
|
|
|
expected := make(map[string]types.AuthConfig)
|
|
|
|
expected[testRegistryHostname] = expectedCredHelperAuth
|
2018-03-05 18:53:52 -05:00
|
|
|
assert.Check(t, is.DeepEqual(expected, authConfigs))
|
|
|
|
assert.Check(t, is.Equal(1, testCredsStore.(*mockNativeStore).GetAllCallCount))
|
|
|
|
assert.Check(t, is.Equal(0, testCredHelper.(*mockNativeStore).GetAllCallCount))
|
2018-01-26 17:38:04 -05:00
|
|
|
}
|
2018-05-28 08:45:08 -04:00
|
|
|
|
2019-10-02 15:41:50 -04:00
|
|
|
func TestLoadFromReaderWithUsernamePassword(t *testing.T) {
|
|
|
|
configFile := New("test-load")
|
|
|
|
defer os.Remove("test-load")
|
|
|
|
|
|
|
|
want := types.AuthConfig{
|
|
|
|
Username: "user",
|
|
|
|
Password: "pass",
|
|
|
|
}
|
2019-10-02 15:41:50 -04:00
|
|
|
|
|
|
|
for _, tc := range []types.AuthConfig{
|
|
|
|
want,
|
2019-10-16 12:59:49 -04:00
|
|
|
{
|
2019-10-02 15:41:50 -04:00
|
|
|
Auth: encodeAuth(&want),
|
2019-10-02 15:41:50 -04:00
|
|
|
},
|
2019-10-02 15:41:50 -04:00
|
|
|
} {
|
|
|
|
cf := ConfigFile{
|
|
|
|
AuthConfigs: map[string]types.AuthConfig{
|
|
|
|
"example.com/foo": tc,
|
|
|
|
},
|
|
|
|
}
|
2019-10-02 15:41:50 -04:00
|
|
|
|
2019-10-02 15:41:50 -04:00
|
|
|
b, err := json.Marshal(cf)
|
|
|
|
assert.NilError(t, err)
|
2019-10-02 15:41:50 -04:00
|
|
|
|
2019-10-02 15:41:50 -04:00
|
|
|
err = configFile.LoadFromReader(bytes.NewReader(b))
|
|
|
|
assert.NilError(t, err)
|
2019-10-02 15:41:50 -04:00
|
|
|
|
2019-10-02 15:41:50 -04:00
|
|
|
got, err := configFile.GetAuthConfig("example.com/foo")
|
|
|
|
assert.NilError(t, err)
|
2019-10-02 15:41:50 -04:00
|
|
|
|
2019-10-02 15:41:50 -04:00
|
|
|
assert.Check(t, is.DeepEqual(want.Username, got.Username))
|
|
|
|
assert.Check(t, is.DeepEqual(want.Password, got.Password))
|
|
|
|
}
|
2019-10-02 15:41:50 -04:00
|
|
|
}
|
|
|
|
|
2018-09-10 16:39:30 -04:00
|
|
|
func TestSave(t *testing.T) {
|
|
|
|
configFile := New("test-save")
|
|
|
|
defer os.Remove("test-save")
|
|
|
|
err := configFile.Save()
|
|
|
|
assert.NilError(t, err)
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err := os.ReadFile("test-save")
|
2018-09-10 16:39:30 -04:00
|
|
|
assert.NilError(t, err)
|
2020-09-29 08:49:47 -04:00
|
|
|
assert.Equal(t, string(cfg), `{
|
|
|
|
"auths": {}
|
|
|
|
}`)
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSaveCustomHTTPHeaders(t *testing.T) {
|
|
|
|
configFile := New(t.Name())
|
|
|
|
defer os.Remove(t.Name())
|
|
|
|
configFile.HTTPHeaders["CUSTOM-HEADER"] = "custom-value"
|
|
|
|
configFile.HTTPHeaders["User-Agent"] = "user-agent 1"
|
|
|
|
configFile.HTTPHeaders["user-agent"] = "user-agent 2"
|
|
|
|
err := configFile.Save()
|
|
|
|
assert.NilError(t, err)
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err := os.ReadFile(t.Name())
|
2020-09-29 08:49:47 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Equal(t, string(cfg), `{
|
|
|
|
"auths": {},
|
|
|
|
"HttpHeaders": {
|
|
|
|
"CUSTOM-HEADER": "custom-value"
|
|
|
|
}
|
|
|
|
}`)
|
2018-09-10 16:39:30 -04:00
|
|
|
}
|
2019-01-31 10:32:25 -05:00
|
|
|
|
2020-07-15 11:59:47 -04:00
|
|
|
func TestSaveWithSymlink(t *testing.T) {
|
|
|
|
dir := fs.NewDir(t, t.Name(), fs.WithFile("real-config.json", `{}`))
|
|
|
|
defer dir.Remove()
|
|
|
|
|
|
|
|
symLink := dir.Join("config.json")
|
|
|
|
realFile := dir.Join("real-config.json")
|
|
|
|
err := os.Symlink(realFile, symLink)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
configFile := New(symLink)
|
|
|
|
|
|
|
|
err = configFile.Save()
|
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
fi, err := os.Lstat(symLink)
|
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Assert(t, fi.Mode()&os.ModeSymlink != 0, "expected %s to be a symlink", symLink)
|
|
|
|
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err := os.ReadFile(symLink)
|
2020-07-15 11:59:47 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}"))
|
|
|
|
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err = os.ReadFile(realFile)
|
2020-07-15 11:59:47 -04:00
|
|
|
assert.NilError(t, err)
|
|
|
|
assert.Check(t, is.Equal(string(cfg), "{\n \"auths\": {}\n}"))
|
|
|
|
}
|
|
|
|
|
2019-01-31 10:32:25 -05:00
|
|
|
func TestPluginConfig(t *testing.T) {
|
|
|
|
configFile := New("test-plugin")
|
|
|
|
defer os.Remove("test-plugin")
|
|
|
|
|
2019-02-18 06:49:41 -05:00
|
|
|
// Populate some initial values
|
|
|
|
configFile.SetPluginConfig("plugin1", "data1", "some string")
|
|
|
|
configFile.SetPluginConfig("plugin1", "data2", "42")
|
|
|
|
configFile.SetPluginConfig("plugin2", "data3", "some other string")
|
2019-01-31 10:32:25 -05:00
|
|
|
|
|
|
|
// Save a config file with some plugin config
|
2019-02-18 06:49:41 -05:00
|
|
|
err := configFile.Save()
|
2019-01-31 10:32:25 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
|
|
|
|
// Read it back and check it has the expected content
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err := os.ReadFile("test-plugin")
|
2019-01-31 10:32:25 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
golden.Assert(t, string(cfg), "plugin-config.golden")
|
|
|
|
|
|
|
|
// Load it, resave and check again that the content is
|
|
|
|
// preserved through a load/save cycle.
|
2019-02-18 06:49:41 -05:00
|
|
|
configFile = New("test-plugin2")
|
2019-01-31 10:32:25 -05:00
|
|
|
defer os.Remove("test-plugin2")
|
2019-02-18 06:49:41 -05:00
|
|
|
assert.NilError(t, configFile.LoadFromReader(bytes.NewReader(cfg)))
|
|
|
|
err = configFile.Save()
|
2019-01-31 10:32:25 -05:00
|
|
|
assert.NilError(t, err)
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err = os.ReadFile("test-plugin2")
|
2019-01-31 10:32:25 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
golden.Assert(t, string(cfg), "plugin-config.golden")
|
|
|
|
|
2019-02-18 06:49:41 -05:00
|
|
|
// Check that the contents was reloaded properly
|
|
|
|
v, ok := configFile.PluginConfig("plugin1", "data1")
|
|
|
|
assert.Assert(t, ok)
|
|
|
|
assert.Equal(t, v, "some string")
|
|
|
|
v, ok = configFile.PluginConfig("plugin1", "data2")
|
|
|
|
assert.Assert(t, ok)
|
|
|
|
assert.Equal(t, v, "42")
|
|
|
|
v, ok = configFile.PluginConfig("plugin1", "data3")
|
|
|
|
assert.Assert(t, !ok)
|
|
|
|
assert.Equal(t, v, "")
|
|
|
|
v, ok = configFile.PluginConfig("plugin2", "data3")
|
|
|
|
assert.Assert(t, ok)
|
|
|
|
assert.Equal(t, v, "some other string")
|
|
|
|
v, ok = configFile.PluginConfig("plugin2", "data4")
|
|
|
|
assert.Assert(t, !ok)
|
|
|
|
assert.Equal(t, v, "")
|
|
|
|
v, ok = configFile.PluginConfig("plugin3", "data5")
|
|
|
|
assert.Assert(t, !ok)
|
|
|
|
assert.Equal(t, v, "")
|
|
|
|
|
|
|
|
// Add, remove and modify
|
|
|
|
configFile.SetPluginConfig("plugin1", "data1", "some replacement string") // replacing a key
|
|
|
|
configFile.SetPluginConfig("plugin1", "data2", "") // deleting a key
|
|
|
|
configFile.SetPluginConfig("plugin1", "data3", "some additional string") // new key
|
|
|
|
configFile.SetPluginConfig("plugin2", "data3", "") // delete the whole plugin, since this was the only data
|
|
|
|
configFile.SetPluginConfig("plugin3", "data5", "a new plugin") // add a new plugin
|
|
|
|
|
|
|
|
err = configFile.Save()
|
2019-01-31 10:32:25 -05:00
|
|
|
assert.NilError(t, err)
|
|
|
|
|
2019-02-18 06:49:41 -05:00
|
|
|
// Read it back and check it has the expected content again
|
2022-02-25 08:36:33 -05:00
|
|
|
cfg, err = os.ReadFile("test-plugin2")
|
2019-01-31 10:32:25 -05:00
|
|
|
assert.NilError(t, err)
|
2019-02-18 06:49:41 -05:00
|
|
|
golden.Assert(t, string(cfg), "plugin-config-2.golden")
|
2019-01-31 10:32:25 -05:00
|
|
|
}
|