diff --git a/pkg/credentialprovider/keyring.go b/pkg/credentialprovider/keyring.go index 0c5b3a0c934..63005f6322b 100644 --- a/pkg/credentialprovider/keyring.go +++ b/pkg/credentialprovider/keyring.go @@ -17,6 +17,9 @@ limitations under the License. package credentialprovider import ( + "crypto/sha256" + "encoding/hex" + "encoding/json" "net" "net/url" "path/filepath" @@ -24,7 +27,9 @@ import ( "strings" "k8s.io/apimachinery/pkg/util/sets" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/klog/v2" + "k8s.io/kubernetes/pkg/features" ) // DockerKeyring tracks a set of docker registry credentials, maintaining a @@ -35,13 +40,13 @@ import ( // most specific match for a given image // - iterating a map does not yield predictable results type DockerKeyring interface { - Lookup(image string) ([]AuthConfig, bool) + Lookup(image string) ([]TrackedAuthConfig, bool) } // BasicDockerKeyring is a trivial map-backed implementation of DockerKeyring type BasicDockerKeyring struct { index []string - creds map[string][]AuthConfig + creds map[string][]TrackedAuthConfig } // providersDockerKeyring is an implementation of DockerKeyring that @@ -50,6 +55,47 @@ type providersDockerKeyring struct { Providers []DockerConfigProvider } +// TrackedAuthConfig wraps the AuthConfig and adds information about the source +// of the credentials. +type TrackedAuthConfig struct { + AuthConfig + AuthConfigHash string + + Source *CredentialSource +} + +// NewTrackedAuthConfig initializes the TrackedAuthConfig structure by adding +// the source information to the supplied AuthConfig. It also counts a hash of the +// AuthConfig and keeps it in the returned structure. +// +// The supplied CredentialSource is only used when the "KubeletEnsureSecretPulledImages" +// is enabled, the same applies for counting the hash. +func NewTrackedAuthConfig(c *AuthConfig, src *CredentialSource) *TrackedAuthConfig { + if c == nil { + panic("cannot construct TrackedAuthConfig with a nil AuthConfig") + } + + authConfig := &TrackedAuthConfig{ + AuthConfig: *c, + } + + if utilfeature.DefaultFeatureGate.Enabled(features.KubeletEnsureSecretPulledImages) { + authConfig.Source = src + authConfig.AuthConfigHash = hashAuthConfig(c) + } + return authConfig +} + +type CredentialSource struct { + Secret SecretCoordinates +} + +type SecretCoordinates struct { + UID string + Namespace string + Name string +} + // AuthConfig contains authorization information for connecting to a Registry // This type mirrors "github.com/docker/docker/api/types.AuthConfig" type AuthConfig struct { @@ -72,11 +118,13 @@ type AuthConfig struct { RegistryToken string `json:"registrytoken,omitempty"` } -// Add add some docker config in basic docker keyring -func (dk *BasicDockerKeyring) Add(cfg DockerConfig) { +// Add inserts the docker config `cfg` into the basic docker keyring. It attaches +// the `src` information that describes where the docker config `cfg` comes from. +// `src` is nil if the docker config is globally available on the node. +func (dk *BasicDockerKeyring) Add(src *CredentialSource, cfg DockerConfig) { if dk.index == nil { dk.index = make([]string, 0) - dk.creds = make(map[string][]AuthConfig) + dk.creds = make(map[string][]TrackedAuthConfig) } for loc, ident := range cfg { creds := AuthConfig{ @@ -111,7 +159,9 @@ func (dk *BasicDockerKeyring) Add(cfg DockerConfig) { } else { key = parsed.Host } - dk.creds[key] = append(dk.creds[key], creds) + trackedCreds := NewTrackedAuthConfig(&creds, src) + + dk.creds[key] = append(dk.creds[key], *trackedCreds) dk.index = append(dk.index, key) } @@ -235,9 +285,9 @@ func URLsMatch(globURL *url.URL, targetURL *url.URL) (bool, error) { // Lookup implements the DockerKeyring method for fetching credentials based on image name. // Multiple credentials may be returned if there are multiple potentially valid credentials // available. This allows for rotation. -func (dk *BasicDockerKeyring) Lookup(image string) ([]AuthConfig, bool) { +func (dk *BasicDockerKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) { // range over the index as iterating over a map does not provide a predictable ordering - ret := []AuthConfig{} + ret := []TrackedAuthConfig{} for _, k := range dk.index { // both k and image are schemeless URLs because even though schemes are allowed // in the credential configurations, we remove them in Add. @@ -257,16 +307,18 @@ func (dk *BasicDockerKeyring) Lookup(image string) ([]AuthConfig, bool) { } } - return []AuthConfig{}, false + return []TrackedAuthConfig{}, false } // Lookup implements the DockerKeyring method for fetching credentials // based on image name. -func (dk *providersDockerKeyring) Lookup(image string) ([]AuthConfig, bool) { +func (dk *providersDockerKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) { keyring := &BasicDockerKeyring{} for _, p := range dk.Providers { - keyring.Add(p.Provide(image)) + // TODO: the source should probably change once we depend on service accounts (KEP-4412). + // Perhaps `Provide()` should return the source modified to accommodate this? + keyring.Add(nil, p.Provide(image)) } return keyring.Lookup(image) @@ -274,13 +326,13 @@ func (dk *providersDockerKeyring) Lookup(image string) ([]AuthConfig, bool) { // FakeKeyring a fake config credentials type FakeKeyring struct { - auth []AuthConfig + auth []TrackedAuthConfig ok bool } // Lookup implements the DockerKeyring method for fetching credentials based on image name // return fake auth and ok -func (f *FakeKeyring) Lookup(image string) ([]AuthConfig, bool) { +func (f *FakeKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) { return f.auth, f.ok } @@ -289,8 +341,8 @@ type UnionDockerKeyring []DockerKeyring // Lookup implements the DockerKeyring method for fetching credentials based on image name. // return each credentials -func (k UnionDockerKeyring) Lookup(image string) ([]AuthConfig, bool) { - authConfigs := []AuthConfig{} +func (k UnionDockerKeyring) Lookup(image string) ([]TrackedAuthConfig, bool) { + authConfigs := []TrackedAuthConfig{} for _, subKeyring := range k { if subKeyring == nil { continue @@ -302,3 +354,14 @@ func (k UnionDockerKeyring) Lookup(image string) ([]AuthConfig, bool) { return authConfigs, (len(authConfigs) > 0) } + +func hashAuthConfig(creds *AuthConfig) string { + credBytes, err := json.Marshal(creds) + if err != nil { + return "" + } + + hash := sha256.New() + hash.Write([]byte(credBytes)) + return hex.EncodeToString(hash.Sum(nil)) +} diff --git a/pkg/credentialprovider/keyring_test.go b/pkg/credentialprovider/keyring_test.go index 8535a7b7271..ba37c48c323 100644 --- a/pkg/credentialprovider/keyring_test.go +++ b/pkg/credentialprovider/keyring_test.go @@ -21,6 +21,10 @@ import ( "fmt" "reflect" "testing" + + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" + "k8s.io/kubernetes/pkg/features" ) func TestURLsMatch(t *testing.T) { @@ -222,7 +226,7 @@ func TestDockerKeyringForGlob(t *testing.T) { if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) } else { - keyring.Add(cfg) + keyring.Add(nil, cfg) } creds, ok := keyring.Lookup(test.targetURL + "/foo/bar") @@ -290,7 +294,7 @@ func TestKeyringMiss(t *testing.T) { if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) } else { - keyring.Add(cfg) + keyring.Add(nil, cfg) } _, ok := keyring.Lookup(test.lookupURL + "/foo/bar") @@ -318,7 +322,7 @@ func TestKeyringMissWithDockerHubCredentials(t *testing.T) { if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) } else { - keyring.Add(cfg) + keyring.Add(nil, cfg) } val, ok := keyring.Lookup("world.mesos.org/foo/bar") @@ -344,7 +348,7 @@ func TestKeyringHitWithUnqualifiedDockerHub(t *testing.T) { if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) } else { - keyring.Add(cfg) + keyring.Add(nil, cfg) } creds, ok := keyring.Lookup("google/docker-registry") @@ -353,7 +357,7 @@ func TestKeyringHitWithUnqualifiedDockerHub(t *testing.T) { return } if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) + t.Errorf("Got more hits than expected: %v", creds) } val := creds[0] @@ -385,7 +389,7 @@ func TestKeyringHitWithUnqualifiedLibraryDockerHub(t *testing.T) { if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) } else { - keyring.Add(cfg) + keyring.Add(nil, cfg) } creds, ok := keyring.Lookup("jenkins") @@ -394,7 +398,7 @@ func TestKeyringHitWithUnqualifiedLibraryDockerHub(t *testing.T) { return } if len(creds) > 1 { - t.Errorf("Got more hits than expected: %s", creds) + t.Errorf("Got more hits than expected: %v", creds) } val := creds[0] @@ -426,7 +430,7 @@ func TestKeyringHitWithQualifiedDockerHub(t *testing.T) { if cfg, err := ReadDockerConfigFileFromBytes([]byte(sampleDockerConfig)); err != nil { t.Errorf("Error processing json blob %q, %v", sampleDockerConfig, err) } else { - keyring.Add(cfg) + keyring.Add(nil, cfg) } creds, ok := keyring.Lookup(url + "/google/docker-registry") @@ -435,7 +439,7 @@ func TestKeyringHitWithQualifiedDockerHub(t *testing.T) { return } if len(creds) > 2 { - t.Errorf("Got more hits than expected: %s", creds) + t.Errorf("Got more hits than expected: %v", creds) } val := creds[0] @@ -498,20 +502,24 @@ func TestProvidersDockerKeyring(t *testing.T) { } func TestDockerKeyringLookup(t *testing.T) { + // turn on the ensure secret pulled images feature to get the hashes with the creds + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletEnsureSecretPulledImages, true) ada := AuthConfig{ Username: "ada", Password: "smash", // Fake value for testing. Email: "ada@example.com", } + adaHash := "353258b53f5e9a57b059eab3f05312fc35bbeb874f08ce101e7bf0bf46977423" grace := AuthConfig{ Username: "grace", Password: "squash", // Fake value for testing. Email: "grace@example.com", } + graceHash := "f949b3837a1eb733a951b6aeda0b3327c09ec50c917de9ca35818e8fbf567e29" dk := &BasicDockerKeyring{} - dk.Add(DockerConfig{ + dk.Add(nil, DockerConfig{ "bar.example.com/pong": DockerConfigEntry{ Username: grace.Username, Password: grace.Password, @@ -526,27 +534,27 @@ func TestDockerKeyringLookup(t *testing.T) { tests := []struct { image string - match []AuthConfig + match []TrackedAuthConfig ok bool }{ // direct match - {"bar.example.com", []AuthConfig{ada}, true}, + {"bar.example.com", []TrackedAuthConfig{{AuthConfig: ada, AuthConfigHash: adaHash}}, true}, // direct match deeper than other possible matches - {"bar.example.com/pong", []AuthConfig{grace, ada}, true}, + {"bar.example.com/pong", []TrackedAuthConfig{{AuthConfig: grace, AuthConfigHash: graceHash}, {AuthConfig: ada, AuthConfigHash: adaHash}}, true}, // no direct match, deeper path ignored - {"bar.example.com/ping", []AuthConfig{ada}, true}, + {"bar.example.com/ping", []TrackedAuthConfig{{AuthConfig: ada, AuthConfigHash: adaHash}}, true}, // match first part of path token - {"bar.example.com/pongz", []AuthConfig{grace, ada}, true}, + {"bar.example.com/pongz", []TrackedAuthConfig{{AuthConfig: grace, AuthConfigHash: graceHash}, {AuthConfig: ada, AuthConfigHash: adaHash}}, true}, // match regardless of sub-path - {"bar.example.com/pong/pang", []AuthConfig{grace, ada}, true}, + {"bar.example.com/pong/pang", []TrackedAuthConfig{{AuthConfig: grace, AuthConfigHash: graceHash}, {AuthConfig: ada, AuthConfigHash: adaHash}}, true}, // no host match - {"example.com", []AuthConfig{}, false}, - {"foo.example.com", []AuthConfig{}, false}, + {"example.com", []TrackedAuthConfig{}, false}, + {"foo.example.com", []TrackedAuthConfig{}, false}, } for i, tt := range tests { @@ -565,14 +573,17 @@ func TestDockerKeyringLookup(t *testing.T) { // by images that only match the hostname. // NOTE: the above covers the case of a more specific match trumping just hostname. func TestIssue3797(t *testing.T) { + // turn on the ensure secret pulled images feature to get the hashes with the creds + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletEnsureSecretPulledImages, true) rex := AuthConfig{ Username: "rex", Password: "tiny arms", // Fake value for testing. Email: "rex@example.com", } + rexHash := "899748fec74c8dd761845fca727f4249b05be275ff24026676fcd4351f656363" dk := &BasicDockerKeyring{} - dk.Add(DockerConfig{ + dk.Add(nil, DockerConfig{ "https://quay.io/v1/": DockerConfigEntry{ Username: rex.Username, Password: rex.Password, @@ -582,15 +593,15 @@ func TestIssue3797(t *testing.T) { tests := []struct { image string - match []AuthConfig + match []TrackedAuthConfig ok bool }{ // direct match - {"quay.io", []AuthConfig{rex}, true}, + {"quay.io", []TrackedAuthConfig{{AuthConfig: rex, AuthConfigHash: rexHash}}, true}, // partial matches - {"quay.io/foo", []AuthConfig{rex}, true}, - {"quay.io/foo/bar", []AuthConfig{rex}, true}, + {"quay.io/foo", []TrackedAuthConfig{{AuthConfig: rex, AuthConfigHash: rexHash}}, true}, + {"quay.io/foo/bar", []TrackedAuthConfig{{AuthConfig: rex, AuthConfigHash: rexHash}}, true}, } for i, tt := range tests { diff --git a/pkg/credentialprovider/secrets/secrets.go b/pkg/credentialprovider/secrets/secrets.go index 423cd2bbf94..eab1e22ab40 100644 --- a/pkg/credentialprovider/secrets/secrets.go +++ b/pkg/credentialprovider/secrets/secrets.go @@ -27,32 +27,52 @@ import ( // then a DockerKeyring is built based on every hit and unioned with the defaultKeyring. // If they do not, then the default keyring is returned func MakeDockerKeyring(passedSecrets []v1.Secret, defaultKeyring credentialprovider.DockerKeyring) (credentialprovider.DockerKeyring, error) { - passedCredentials := []credentialprovider.DockerConfig{} - for _, passedSecret := range passedSecrets { + providerFromSecrets, err := secretsToTrackedDockerConfigs(passedSecrets) + if err != nil { + return nil, err + } + + if providerFromSecrets == nil { + return defaultKeyring, nil + } + + return credentialprovider.UnionDockerKeyring{providerFromSecrets, defaultKeyring}, nil +} + +func secretsToTrackedDockerConfigs(secrets []v1.Secret) (credentialprovider.DockerKeyring, error) { + provider := &credentialprovider.BasicDockerKeyring{} + validSecretsFound := 0 + for _, passedSecret := range secrets { if dockerConfigJSONBytes, dockerConfigJSONExists := passedSecret.Data[v1.DockerConfigJsonKey]; (passedSecret.Type == v1.SecretTypeDockerConfigJson) && dockerConfigJSONExists && (len(dockerConfigJSONBytes) > 0) { dockerConfigJSON := credentialprovider.DockerConfigJSON{} if err := json.Unmarshal(dockerConfigJSONBytes, &dockerConfigJSON); err != nil { return nil, err } - passedCredentials = append(passedCredentials, dockerConfigJSON.Auths) + coords := credentialprovider.SecretCoordinates{ + UID: string(passedSecret.UID), + Namespace: passedSecret.Namespace, + Name: passedSecret.Name} + + provider.Add(&credentialprovider.CredentialSource{Secret: coords}, dockerConfigJSON.Auths) + validSecretsFound++ } else if dockercfgBytes, dockercfgExists := passedSecret.Data[v1.DockerConfigKey]; (passedSecret.Type == v1.SecretTypeDockercfg) && dockercfgExists && (len(dockercfgBytes) > 0) { dockercfg := credentialprovider.DockerConfig{} if err := json.Unmarshal(dockercfgBytes, &dockercfg); err != nil { return nil, err } - passedCredentials = append(passedCredentials, dockercfg) + coords := credentialprovider.SecretCoordinates{ + UID: string(passedSecret.UID), + Namespace: passedSecret.Namespace, + Name: passedSecret.Name} + provider.Add(&credentialprovider.CredentialSource{Secret: coords}, dockercfg) + validSecretsFound++ } } - if len(passedCredentials) > 0 { - basicKeyring := &credentialprovider.BasicDockerKeyring{} - for _, currCredentials := range passedCredentials { - basicKeyring.Add(currCredentials) - } - return credentialprovider.UnionDockerKeyring{basicKeyring, defaultKeyring}, nil + if validSecretsFound == 0 { + return nil, nil } - - return defaultKeyring, nil + return provider, nil } diff --git a/pkg/credentialprovider/secrets/secrets_test.go b/pkg/credentialprovider/secrets/secrets_test.go index ad03cdd9dda..e96112fd3b3 100644 --- a/pkg/credentialprovider/secrets/secrets_test.go +++ b/pkg/credentialprovider/secrets/secrets_test.go @@ -20,107 +20,145 @@ import ( "reflect" "testing" - "k8s.io/api/core/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/credentialprovider" + "k8s.io/kubernetes/pkg/features" ) -// fakeKeyring is a fake docker auth config keyring -type fakeKeyring struct { - auth []credentialprovider.AuthConfig +// FakeKeyring a fake config credentials +type FakeKeyring struct { + auth []credentialprovider.TrackedAuthConfig ok bool } -// Lookup implements the DockerKeyring method for fetching credentials based on image name. -// Returns fake results based on the auth and ok fields in fakeKeyring -func (f *fakeKeyring) Lookup(image string) ([]credentialprovider.AuthConfig, bool) { +// Lookup implements the DockerKeyring method for fetching credentials based on image name +// return fake auth and ok +func (f *FakeKeyring) Lookup(image string) ([]credentialprovider.TrackedAuthConfig, bool) { return f.auth, f.ok } func Test_MakeDockerKeyring(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.KubeletEnsureSecretPulledImages, true) + testcases := []struct { - name string - image string - defaultKeyring credentialprovider.DockerKeyring - pullSecrets []v1.Secret - authConfigs []credentialprovider.AuthConfig - found bool + name string + image string + defaultKeyring credentialprovider.DockerKeyring + pullSecrets []v1.Secret + expectedAuthConfigs []credentialprovider.TrackedAuthConfig + found bool }{ { - name: "with .dockerconfigjson and auth field", - image: "test.registry.io", - defaultKeyring: &fakeKeyring{}, + name: "with .dockerconfigjson and auth field", + image: "test.registry.io", pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{ v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "user", - Password: "password", + Source: &credentialprovider.CredentialSource{ + Secret: credentialprovider.SecretCoordinates{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, + }, + AuthConfig: credentialprovider.AuthConfig{ + Username: "user", + Password: "password", + }, + AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d", }, }, found: true, }, { - name: "with .dockerconfig and auth field", - image: "test.registry.io", - defaultKeyring: &fakeKeyring{}, + name: "with .dockerconfig and auth field", + image: "test.registry.io", pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockercfg, Data: map[string][]byte{ v1.DockerConfigKey: []byte(`{"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "user", - Password: "password", + Source: &credentialprovider.CredentialSource{ + Secret: credentialprovider.SecretCoordinates{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, + }, + AuthConfig: credentialprovider.AuthConfig{ + Username: "user", + Password: "password", + }, + AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d", }, }, found: true, }, { - name: "with .dockerconfigjson and username/password fields", - image: "test.registry.io", - defaultKeyring: &fakeKeyring{}, + name: "with .dockerconfigjson and username/password fields", + image: "test.registry.io", pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{ v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"username": "user", "password": "password"}}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "user", - Password: "password", + Source: &credentialprovider.CredentialSource{ + Secret: credentialprovider.SecretCoordinates{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, + }, + AuthConfig: credentialprovider.AuthConfig{ + Username: "user", + Password: "password", + }, + AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d", }, }, found: true, }, { - name: "with .dockerconfig and username/password fields", - image: "test.registry.io", - defaultKeyring: &fakeKeyring{}, + name: "with .dockerconfig and username/password fields", + image: "test.registry.io", pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockercfg, Data: map[string][]byte{ v1.DockerConfigKey: []byte(`{"test.registry.io": {"username": "user", "password": "password"}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "user", - Password: "password", + Source: &credentialprovider.CredentialSource{ + Secret: credentialprovider.SecretCoordinates{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, + }, + AuthConfig: credentialprovider.AuthConfig{ + Username: "user", + Password: "password", + }, + AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d", }, }, found: true, @@ -128,56 +166,65 @@ func Test_MakeDockerKeyring(t *testing.T) { { name: "with .dockerconfigjson but with wrong Secret Type", image: "test.registry.io", - defaultKeyring: &fakeKeyring{}, + defaultKeyring: &FakeKeyring{}, pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockercfg, Data: map[string][]byte{ v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`), }, }, }, - authConfigs: nil, - found: false, + found: false, }, { name: "with .dockerconfig but with wrong Secret Type", image: "test.registry.io", - defaultKeyring: &fakeKeyring{}, + defaultKeyring: &FakeKeyring{}, pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{ v1.DockerConfigKey: []byte(`{"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}`), }, }, }, - authConfigs: nil, - found: false, + found: false, }, { name: "with not matcing .dockerconfigjson and default keyring", image: "test.registry.io", - defaultKeyring: &fakeKeyring{ - auth: []credentialprovider.AuthConfig{ + defaultKeyring: &FakeKeyring{ + auth: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, }, }, }, pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{ v1.DockerConfigJsonKey: []byte(`{"auths": {"foobar.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, + AuthConfigHash: "", }, }, found: true, @@ -185,26 +232,33 @@ func Test_MakeDockerKeyring(t *testing.T) { { name: "with not matching .dockerconfig and default keyring", image: "test.registry.io", - defaultKeyring: &fakeKeyring{ - auth: []credentialprovider.AuthConfig{ + defaultKeyring: &FakeKeyring{ + auth: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, }, }, }, pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockercfg, Data: map[string][]byte{ v1.DockerConfigKey: []byte(`{"foobar.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, + AuthConfigHash: "", }, }, found: true, @@ -212,19 +266,24 @@ func Test_MakeDockerKeyring(t *testing.T) { { name: "with no pull secrets but has default keyring", image: "test.registry.io", - defaultKeyring: &fakeKeyring{ - auth: []credentialprovider.AuthConfig{ + defaultKeyring: &FakeKeyring{ + auth: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, }, }, }, pullSecrets: []v1.Secret{}, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, + AuthConfigHash: "", }, }, found: false, @@ -232,30 +291,44 @@ func Test_MakeDockerKeyring(t *testing.T) { { name: "with pull secrets and has default keyring", image: "test.registry.io", - defaultKeyring: &fakeKeyring{ - auth: []credentialprovider.AuthConfig{ + defaultKeyring: &FakeKeyring{ + auth: []credentialprovider.TrackedAuthConfig{ { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, }, }, }, pullSecrets: []v1.Secret{ { + ObjectMeta: metav1.ObjectMeta{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, Type: v1.SecretTypeDockerConfigJson, Data: map[string][]byte{ v1.DockerConfigJsonKey: []byte(`{"auths": {"test.registry.io": {"auth": "dXNlcjpwYXNzd29yZA=="}}}`), }, }, }, - authConfigs: []credentialprovider.AuthConfig{ + expectedAuthConfigs: []credentialprovider.TrackedAuthConfig{ { - Username: "user", - Password: "password", + Source: &credentialprovider.CredentialSource{ + Secret: credentialprovider.SecretCoordinates{ + Name: "s1", Namespace: "ns1", UID: "uid1"}, + }, + AuthConfig: credentialprovider.AuthConfig{ + Username: "user", + Password: "password", + }, + AuthConfigHash: "a55436fc140d516560d072c5fe8700385ce9f41629abf65c1edcbcb39fac691d", }, { - Username: "default-user", - Password: "default-password", + AuthConfig: credentialprovider.AuthConfig{ + Username: "default-user", + Password: "default-password", + }, + AuthConfigHash: "", }, }, found: true, @@ -276,9 +349,9 @@ func Test_MakeDockerKeyring(t *testing.T) { t.Errorf("unexpected lookup for image: %s", testcase.image) } - if !reflect.DeepEqual(authConfigs, testcase.authConfigs) { + if !reflect.DeepEqual(authConfigs, testcase.expectedAuthConfigs) { // TODO: we may need better comparison as the result is unordered t.Logf("actual auth configs: %#v", authConfigs) - t.Logf("expected auth configs: %#v", testcase.authConfigs) + t.Logf("expected auth configs: %#v", testcase.expectedAuthConfigs) t.Error("auth configs did not match") } }) diff --git a/pkg/kubelet/kuberuntime/kuberuntime_image_test.go b/pkg/kubelet/kuberuntime/kuberuntime_image_test.go index 3dd38b6b7d4..a901189bbb8 100644 --- a/pkg/kubelet/kuberuntime/kuberuntime_image_test.go +++ b/pkg/kubelet/kuberuntime/kuberuntime_image_test.go @@ -320,7 +320,7 @@ func TestPullWithSecrets(t *testing.T) { } for description, test := range tests { builtInKeyRing := &credentialprovider.BasicDockerKeyring{} - builtInKeyRing.Add(test.builtInDockerConfig) + builtInKeyRing.Add(nil, test.builtInDockerConfig) _, fakeImageService, fakeManager, err := customTestRuntimeManager(builtInKeyRing) require.NoError(t, err)