diff --git a/pkg/authn/k8schain/k8schain.go b/pkg/authn/k8schain/k8schain.go index 00cec9008..7055e51d8 100644 --- a/pkg/authn/k8schain/k8schain.go +++ b/pkg/authn/k8schain/k8schain.go @@ -23,7 +23,7 @@ import ( "github.com/google/go-containerregistry/pkg/name" credentialprovider "github.com/vdemeester/k8s-pkg-credentialprovider" credentialprovidersecrets "github.com/vdemeester/k8s-pkg-credentialprovider/secrets" - v1 "k8s.io/api/core/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -66,7 +66,7 @@ func New(ctx context.Context, client kubernetes.Interface, opt Options) (authn.K // Pod's service account. // First, fetch all of the explicitly declared pull secrets - var pullSecrets []v1.Secret + var pullSecrets []corev1.Secret if client != nil { for _, name := range opt.ImagePullSecrets { ps, err := client.CoreV1().Secrets(opt.Namespace).Get(ctx, name, metav1.GetOptions{}) @@ -90,18 +90,7 @@ func New(ctx context.Context, client kubernetes.Interface, opt Options) (authn.K } } - once.Do(func() { - keyring = credentialprovider.NewDockerKeyring() - }) - - // Third, extend the default keyring with the pull secrets. - kr, err := credentialprovidersecrets.MakeDockerKeyring(pullSecrets, keyring) - if err != nil { - return nil, err - } - return &keychain{ - keyring: kr, - }, nil + return NewFromPullSecrets(ctx, pullSecrets) } // NewInCluster returns a new authn.Keychain suitable for resolving image references as @@ -132,6 +121,23 @@ func NewNoClient(ctx context.Context) (authn.Keychain, error) { return New(ctx, nil, Options{}) } +// NewFromPullSecrets returns a new authn.Keychain suitable for resolving image references as +// scoped by the pull secrets. +func NewFromPullSecrets(ctx context.Context, pullSecrets []corev1.Secret) (authn.Keychain, error) { + once.Do(func() { + keyring = credentialprovider.NewDockerKeyring() + }) + + // Extend the default keyring with the pull secrets. + kr, err := credentialprovidersecrets.MakeDockerKeyring(pullSecrets, keyring) + if err != nil { + return nil, err + } + return &keychain{ + keyring: kr, + }, nil +} + type lazyProvider struct { kc *keychain image string diff --git a/pkg/authn/k8schain/k8schain_test.go b/pkg/authn/k8schain/k8schain_test.go index e9a2bdf95..24dfc6f3b 100644 --- a/pkg/authn/k8schain/k8schain_test.go +++ b/pkg/authn/k8schain/k8schain_test.go @@ -178,3 +178,68 @@ func TestImagePullSecrets(t *testing.T) { }) } } + +func TestFromPullSecrets(t *testing.T) { + username, password := "foo", "bar" + specificUser, specificPass := "very", "specific" + + pullSecrets := []corev1.Secret{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "secret", + Namespace: "ns", + }, + Type: corev1.SecretTypeDockercfg, + Data: map[string][]byte{ + corev1.DockerConfigKey: []byte( + fmt.Sprintf(`{"fake.registry.io": {"auth": %q}, "fake.registry.io/more/specific": {"auth": %q}}`, + base64.StdEncoding.EncodeToString([]byte(username+":"+password)), + base64.StdEncoding.EncodeToString([]byte(specificUser+":"+specificPass))), + ), + }, + }, + } + + kc, err := NewFromPullSecrets(context.Background(), pullSecrets) + if err != nil { + t.Fatalf("NewFromPullSecrets() = %v", err) + } + + repo, err := name.NewRepository("fake.registry.io/more/specific", name.WeakValidation) + if err != nil { + t.Errorf("NewRegistry() = %v", err) + } + + for _, tc := range []struct { + name string + auth authn.Authenticator + target authn.Resource + }{{ + name: "registry", + auth: &authn.Basic{Username: username, Password: password}, + target: repo.Registry, + }, { + name: "repo", + auth: &authn.Basic{Username: specificUser, Password: specificPass}, + target: repo, + }} { + t.Run(tc.name, func(t *testing.T) { + tc := tc + auth, err := kc.Resolve(tc.target) + if err != nil { + t.Errorf("Resolve(%v) = %v", tc.target, err) + } + got, err := auth.Authorization() + if err != nil { + t.Errorf("Authorization() = %v", err) + } + want, err := tc.auth.Authorization() + if err != nil { + t.Errorf("Authorization() = %v", err) + } + if !reflect.DeepEqual(got, want) { + t.Errorf("Resolve() = %v, want %v", got, want) + } + }) + } +}