diff --git a/pkg/credentialprovider/shim/shim.go b/pkg/credentialprovider/shim/shim.go index 95a54e7..3bc2fbc 100644 --- a/pkg/credentialprovider/shim/shim.go +++ b/pkg/credentialprovider/shim/shim.go @@ -42,7 +42,7 @@ func init() { type shimProvider struct { cfg *v1alpha1.KubeletImageCredentialProviderShimConfig - providersMutex sync.Mutex + providersMutex sync.RWMutex providers map[string]Provider } @@ -117,13 +117,14 @@ func (p *shimProvider) registerCredentialProvider(name string, provider *pluginP p.providers[name] = provider } -var ErrTooManyCredentials = errors.New("too many credentials") - func (p *shimProvider) GetCredentials( _ context.Context, img string, _ []string, ) (*credentialproviderv1beta1.CredentialProviderResponse, error) { + p.providersMutex.RLock() + defer p.providersMutex.RUnlock() + authMap := map[string]credentialproviderv1beta1.AuthConfig{} mirrorAuthConfig, cacheDuration, mirrorAuthFound, err := p.getMirrorCredentialsForImage(img) diff --git a/pkg/credentialprovider/shim/shim_test.go b/pkg/credentialprovider/shim/shim_test.go index c18f1ba..6f602c8 100644 --- a/pkg/credentialprovider/shim/shim_test.go +++ b/pkg/credentialprovider/shim/shim_test.go @@ -1,48 +1,131 @@ // Copyright 2022 D2iQ, Inc. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -package shim +package shim_test import ( "context" - "encoding/json" - "log" - "os" "path/filepath" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + credentialproviderv1beta1 "k8s.io/kubelet/pkg/apis/credentialprovider/v1beta1" + + "github.com/mesosphere/kubelet-image-credential-provider-shim/pkg/credentialprovider/shim" ) -func ExampleCredentialProvider_GetCredentials() { - p, err := NewProviderFromConfigFile(filepath.Join("testdata", "config.yaml")) - if err != nil { - log.Fatal(err) //nolint:revive // Allow fatal in examples. - } +func Test_shimProvider_GetCredentials(t *testing.T) { + //nolint:revive // Dummy duration ok in tests. + expectedDummyDuration := 5 * time.Second - creds, err := p.GetCredentials(context.Background(), "img.v1beta1/abc/def:v1.2.3", nil) - if err != nil { - log.Fatal(err) //nolint:revive // Allow fatal in examples. - } + const ( + dummyImage = "img.v1beta1/abc/def:v1.2.3" + mirrorUser = "mirroruser" + mirrorPassword = "mirrorpassword" + wildcardDomain = "*.*" + credentialProviderResponseKind = "CredentialProviderResponse" //nolint:gosec // No actual credentials here. + ) - if err := json.NewEncoder(os.Stdout).Encode(creds); err != nil { - log.Fatal(err) //nolint:revive // Allow fatal in examples. - } - //nolint:lll // Long example output. - // Output: {"kind":"CredentialProviderResponse","apiVersion":"credentialprovider.kubelet.k8s.io/v1beta1","cacheKeyType":"Image","cacheDuration":"5s","auth":{"img.v1beta1/abc/def:v1.2.3":{"username":"v1beta1user","password":"v1beta1password"}}} -} + t.Parallel() + tests := []struct { + name string + cfgFile string + img string + want *credentialproviderv1beta1.CredentialProviderResponse + wantErr error + }{{ + name: "mirror only", + cfgFile: filepath.Join("testdata", "config-with-mirror-only.yaml"), + img: dummyImage, + want: &credentialproviderv1beta1.CredentialProviderResponse{ + TypeMeta: v1.TypeMeta{ + APIVersion: credentialproviderv1beta1.SchemeGroupVersion.String(), + Kind: credentialProviderResponseKind, + }, + CacheKeyType: credentialproviderv1beta1.ImagePluginCacheKeyType, + CacheDuration: &v1.Duration{Duration: expectedDummyDuration}, + Auth: map[string]credentialproviderv1beta1.AuthConfig{ + dummyImage: {Username: mirrorUser, Password: mirrorPassword}, + }, + }, + }, { + name: "mirror first", + cfgFile: filepath.Join("testdata", "config-with-mirror-first.yaml"), + img: dummyImage, + want: &credentialproviderv1beta1.CredentialProviderResponse{ + TypeMeta: v1.TypeMeta{ + APIVersion: credentialproviderv1beta1.SchemeGroupVersion.String(), + Kind: credentialProviderResponseKind, + }, + CacheKeyType: credentialproviderv1beta1.ImagePluginCacheKeyType, + CacheDuration: &v1.Duration{Duration: expectedDummyDuration}, + Auth: map[string]credentialproviderv1beta1.AuthConfig{ + dummyImage: {Username: mirrorUser, Password: mirrorPassword}, + wildcardDomain: {Username: "v1beta1user", Password: "v1beta1password"}, + }, + }, + }, { + name: "mirror last", + cfgFile: filepath.Join("testdata", "config-with-mirror-last.yaml"), + img: dummyImage, + want: &credentialproviderv1beta1.CredentialProviderResponse{ + TypeMeta: v1.TypeMeta{ + APIVersion: credentialproviderv1beta1.SchemeGroupVersion.String(), + Kind: credentialProviderResponseKind, + }, + CacheKeyType: credentialproviderv1beta1.ImagePluginCacheKeyType, + CacheDuration: &v1.Duration{Duration: expectedDummyDuration}, + Auth: map[string]credentialproviderv1beta1.AuthConfig{ + wildcardDomain: {Username: mirrorUser, Password: mirrorPassword}, + dummyImage: {Username: "v1beta1user", Password: "v1beta1password"}, + }, + }, + }, { + name: "mirror and no matching origin", + cfgFile: filepath.Join("testdata", "config-with-mirror-last.yaml"), + img: "noorigin/image:v1.2.3.4", + want: &credentialproviderv1beta1.CredentialProviderResponse{ + TypeMeta: v1.TypeMeta{ + APIVersion: credentialproviderv1beta1.SchemeGroupVersion.String(), + Kind: credentialProviderResponseKind, + }, + CacheKeyType: credentialproviderv1beta1.ImagePluginCacheKeyType, + CacheDuration: &v1.Duration{Duration: expectedDummyDuration}, + Auth: map[string]credentialproviderv1beta1.AuthConfig{ + wildcardDomain: {Username: mirrorUser, Password: mirrorPassword}, + }, + }, + }, { + name: "no mirror", + cfgFile: filepath.Join("testdata", "config-no-mirror.yaml"), + img: dummyImage, + want: &credentialproviderv1beta1.CredentialProviderResponse{ + TypeMeta: v1.TypeMeta{ + APIVersion: credentialproviderv1beta1.SchemeGroupVersion.String(), + Kind: credentialProviderResponseKind, + }, + CacheKeyType: credentialproviderv1beta1.ImagePluginCacheKeyType, + CacheDuration: &v1.Duration{Duration: expectedDummyDuration}, + Auth: map[string]credentialproviderv1beta1.AuthConfig{ + dummyImage: {Username: "v1beta1user", Password: "v1beta1password"}, + }, + }, + }} + for _, tt := range tests { + tt := tt // Capture range variable. -func ExampleCredentialProvider_GetCredentials_withMirror() { - p, err := NewProviderFromConfigFile(filepath.Join("testdata", "config-with-mirror.yaml")) - if err != nil { - log.Fatal(err) //nolint:revive // Allow fatal in examples. - } + t.Run(tt.name, func(t *testing.T) { + t.Parallel() - creds, err := p.GetCredentials(context.Background(), "img.v1beta1/abc/def:v1.2.3", nil) - if err != nil { - log.Fatal(err) //nolint:revive // Allow fatal in examples. - } + p, err := shim.NewProviderFromConfigFile(tt.cfgFile) + require.NoError(t, err) - if err := json.NewEncoder(os.Stdout).Encode(creds); err != nil { - log.Fatal(err) //nolint:revive // Allow fatal in examples. + got, err := p.GetCredentials(context.Background(), tt.img, nil) + require.ErrorIs(t, err, tt.wantErr) + assert.Equal(t, tt.want, got) + }) } - //nolint:lll // Long example output. - // Output: {"kind":"CredentialProviderResponse","apiVersion":"credentialprovider.kubelet.k8s.io/v1beta1","cacheKeyType":"Image","cacheDuration":"5s","auth":{"*.*":{"username":"v1beta1user","password":"v1beta1password"},"img.v1beta1/abc/def:v1.2.3":{"username":"mirroruser","password":"mirrorpassword"}}} } diff --git a/pkg/credentialprovider/shim/testdata/config.yaml b/pkg/credentialprovider/shim/testdata/config-no-mirror.yaml similarity index 100% rename from pkg/credentialprovider/shim/testdata/config.yaml rename to pkg/credentialprovider/shim/testdata/config-no-mirror.yaml diff --git a/pkg/credentialprovider/shim/testdata/config-with-mirror.yaml b/pkg/credentialprovider/shim/testdata/config-with-mirror-first.yaml similarity index 100% rename from pkg/credentialprovider/shim/testdata/config-with-mirror.yaml rename to pkg/credentialprovider/shim/testdata/config-with-mirror-first.yaml diff --git a/pkg/credentialprovider/shim/testdata/config-with-mirror-last.yaml b/pkg/credentialprovider/shim/testdata/config-with-mirror-last.yaml new file mode 100644 index 0000000..7f85785 --- /dev/null +++ b/pkg/credentialprovider/shim/testdata/config-with-mirror-last.yaml @@ -0,0 +1,25 @@ +# Copyright 2022 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: config.kubeletimagecredentialprovidershim.d2iq.com/v1alpha1 +kind: KubeletImageCredentialProviderShimConfig +mirror: + endpoint: someregistry.com + credentialsStrategy: MirrorCredentialsLast +credentialProviderPluginBinDir: testdata +credentialProviders: + apiVersion: kubelet.config.k8s.io/v1beta1 + kind: CredentialProviderConfig + providers: + - name: staticcredentialprovider-v1alpha1.sh + matchImages: + - '*.v1alpha1' + apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1 + - name: staticcredentialprovider-v1beta1.sh + matchImages: + - '*.v1beta1' + apiVersion: credentialprovider.kubelet.k8s.io/v1beta1 + - name: staticcredentialprovider-mirror.sh + matchImages: + - someregistry.com + apiVersion: credentialprovider.kubelet.k8s.io/v1beta1 diff --git a/pkg/credentialprovider/shim/testdata/config-with-mirror-only.yaml b/pkg/credentialprovider/shim/testdata/config-with-mirror-only.yaml new file mode 100644 index 0000000..a943829 --- /dev/null +++ b/pkg/credentialprovider/shim/testdata/config-with-mirror-only.yaml @@ -0,0 +1,25 @@ +# Copyright 2022 D2iQ, Inc. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +apiVersion: config.kubeletimagecredentialprovidershim.d2iq.com/v1alpha1 +kind: KubeletImageCredentialProviderShimConfig +mirror: + endpoint: someregistry.com + credentialsStrategy: MirrorCredentialsOnly +credentialProviderPluginBinDir: testdata +credentialProviders: + apiVersion: kubelet.config.k8s.io/v1beta1 + kind: CredentialProviderConfig + providers: + - name: staticcredentialprovider-v1alpha1.sh + matchImages: + - '*.v1alpha1' + apiVersion: credentialprovider.kubelet.k8s.io/v1alpha1 + - name: staticcredentialprovider-v1beta1.sh + matchImages: + - '*.v1beta1' + apiVersion: credentialprovider.kubelet.k8s.io/v1beta1 + - name: staticcredentialprovider-mirror.sh + matchImages: + - someregistry.com + apiVersion: credentialprovider.kubelet.k8s.io/v1beta1