Skip to content

Commit b02d448

Browse files
authored
Update Keychain interface to accept repo (#510)
* Update Keychain interface to accept repo Fixes #495 * Update pkg/authn/k8schain/k8schain_test.go Co-Authored-By: Matt Moore <[email protected]> * Address feedback * Target -> Resource
1 parent a8228cd commit b02d448

15 files changed

+86
-59
lines changed

pkg/authn/helper.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import (
2020
"fmt"
2121
"os/exec"
2222
"strings"
23-
24-
"github.com/google/go-containerregistry/pkg/name"
2523
)
2624

2725
// magicNotFoundMessage is the string that the CLI special cases to mean
@@ -46,7 +44,7 @@ func (dr *defaultRunner) Run(cmd *exec.Cmd) error {
4644
// helper executes the named credential helper against the given domain.
4745
type helper struct {
4846
name string
49-
domain name.Registry
47+
domain string
5048

5149
// We add this layer of indirection to facilitate unit testing.
5250
r runner

pkg/authn/helper_test.go

+1-3
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,10 @@ import (
1818
"errors"
1919
"os/exec"
2020
"testing"
21-
22-
"github.com/google/go-containerregistry/pkg/name"
2321
)
2422

2523
var (
26-
testDomain, _ = name.NewRegistry("foo.dev", name.WeakValidation)
24+
testDomain = "foo.dev"
2725
)
2826

2927
// errorRunner implements runner to always return an execution error.

pkg/authn/k8schain/k8schain.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -153,10 +153,17 @@ type keychain struct {
153153
}
154154

155155
// Resolve implements authn.Keychain
156-
func (kc *keychain) Resolve(reg name.Registry) (authn.Authenticator, error) {
157-
// TODO(mattmoor): Lookup expects an image reference and we only have a registry,
158-
// find something better than this.
159-
creds, found := kc.keyring.Lookup(reg.String() + "/foo/bar")
156+
func (kc *keychain) Resolve(target authn.Resource) (authn.Authenticator, error) {
157+
var (
158+
creds []credentialprovider.LazyAuthConfiguration
159+
found bool
160+
)
161+
if repo, ok := target.(name.Repository); ok {
162+
creds, found = kc.keyring.Lookup(repo.String())
163+
} else {
164+
// Lookup expects an image reference and we only have a registry.
165+
creds, found = kc.keyring.Lookup(target.RegistryStr() + "/foo/bar")
166+
}
160167
if !found || len(creds) < 1 {
161168
return authn.Anonymous, nil
162169
}

pkg/authn/k8schain/k8schain_test.go

+36-17
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func TestAttachedServiceAccount(t *testing.T) {
109109

110110
func TestImagePullSecrets(t *testing.T) {
111111
username, password := "foo", "bar"
112+
specificUser, specificPass := "very", "specific"
112113
client := fakeclient.NewSimpleClientset(&corev1.ServiceAccount{
113114
ObjectMeta: metav1.ObjectMeta{
114115
Name: "default",
@@ -122,8 +123,9 @@ func TestImagePullSecrets(t *testing.T) {
122123
Type: corev1.SecretTypeDockercfg,
123124
Data: map[string][]byte{
124125
corev1.DockerConfigKey: []byte(
125-
fmt.Sprintf(`{"fake.registry.io": {"auth": "%s"}}`,
126-
base64.StdEncoding.EncodeToString([]byte(username+":"+password))),
126+
fmt.Sprintf(`{"fake.registry.io": {"auth": %q}, "fake.registry.io/more/specific": {"auth": %q}}`,
127+
base64.StdEncoding.EncodeToString([]byte(username+":"+password)),
128+
base64.StdEncoding.EncodeToString([]byte(specificUser+":"+specificPass))),
127129
),
128130
},
129131
})
@@ -136,24 +138,41 @@ func TestImagePullSecrets(t *testing.T) {
136138
t.Fatalf("New() = %v", err)
137139
}
138140

139-
reg, err := name.NewRegistry("fake.registry.io", name.WeakValidation)
141+
repo, err := name.NewRepository("fake.registry.io/more/specific", name.WeakValidation)
140142
if err != nil {
141143
t.Errorf("NewRegistry() = %v", err)
142144
}
143145

144-
auth, err := kc.Resolve(reg)
145-
if err != nil {
146-
t.Errorf("Resolve(%v) = %v", reg, err)
147-
}
148-
got, err := auth.Authorization()
149-
if err != nil {
150-
t.Errorf("Authorization() = %v", err)
151-
}
152-
want, err := (&authn.Basic{Username: username, Password: password}).Authorization()
153-
if err != nil {
154-
t.Errorf("Authorization() = %v", err)
155-
}
156-
if got != want {
157-
t.Errorf("Resolve() = %v, want %v", got, want)
146+
for _, tc := range []struct {
147+
name string
148+
auth authn.Authenticator
149+
target authn.Resource
150+
}{{
151+
name: "registry",
152+
auth: &authn.Basic{Username: username, Password: password},
153+
target: repo.Registry,
154+
}, {
155+
name: "repo",
156+
auth: &authn.Basic{Username: specificUser, Password: specificPass},
157+
target: repo,
158+
}} {
159+
t.Run(tc.name, func(t *testing.T) {
160+
tc := tc
161+
auth, err := kc.Resolve(tc.target)
162+
if err != nil {
163+
t.Errorf("Resolve(%v) = %v", tc.target, err)
164+
}
165+
got, err := auth.Authorization()
166+
if err != nil {
167+
t.Errorf("Authorization() = %v", err)
168+
}
169+
want, err := tc.auth.Authorization()
170+
if err != nil {
171+
t.Errorf("Authorization() = %v", err)
172+
}
173+
if got != want {
174+
t.Errorf("Resolve() = %v, want %v", got, want)
175+
}
176+
})
158177
}
159178
}

pkg/authn/keychain.go

+19-8
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,24 @@ import (
2424
"runtime"
2525

2626
"github.com/google/go-containerregistry/pkg/logs"
27-
"github.com/google/go-containerregistry/pkg/name"
2827
)
2928

29+
// Resource represents a registry or repository that can be authenticated against.
30+
type Resource interface {
31+
// String returns the full string representation of the target, e.g.
32+
// gcr.io/my-project or just gcr.io.
33+
String() string
34+
35+
// RegistryStr returns just the registry portion of the target, e.g. for
36+
// gcr.io/my-project, this should just return gcr.io. This is needed to
37+
// pull out an appropriate hostname.
38+
RegistryStr() string
39+
}
40+
3041
// Keychain is an interface for resolving an image reference to a credential.
3142
type Keychain interface {
32-
// Resolve looks up the most appropriate credential for the specified registry.
33-
Resolve(name.Registry) (Authenticator, error)
43+
// Resolve looks up the most appropriate credential for the specified target.
44+
Resolve(Resource) (Authenticator, error)
3445
}
3546

3647
// defaultKeychain implements Keychain with the semantics of the standard Docker
@@ -97,7 +108,7 @@ var (
97108
)
98109

99110
// Resolve implements Keychain.
100-
func (dk *defaultKeychain) Resolve(reg name.Registry) (Authenticator, error) {
111+
func (dk *defaultKeychain) Resolve(target Resource) (Authenticator, error) {
101112
dir, err := configDir()
102113
if err != nil {
103114
logs.Warn.Printf("Unable to determine config dir: %v", err)
@@ -119,21 +130,21 @@ func (dk *defaultKeychain) Resolve(reg name.Registry) (Authenticator, error) {
119130
// Per-registry credential helpers take precedence.
120131
if cf.CredHelper != nil {
121132
for _, form := range domainForms {
122-
if entry, ok := cf.CredHelper[fmt.Sprintf(form, reg.Name())]; ok {
123-
return &helper{name: entry, domain: reg, r: &defaultRunner{}}, nil
133+
if entry, ok := cf.CredHelper[fmt.Sprintf(form, target.RegistryStr())]; ok {
134+
return &helper{name: entry, domain: target.RegistryStr(), r: &defaultRunner{}}, nil
124135
}
125136
}
126137
}
127138

128139
// A global credential helper is next in precedence.
129140
if cf.CredStore != "" {
130-
return &helper{name: cf.CredStore, domain: reg, r: &defaultRunner{}}, nil
141+
return &helper{name: cf.CredStore, domain: target.RegistryStr(), r: &defaultRunner{}}, nil
131142
}
132143

133144
// Lastly, the 'auths' section directly contains basic auth entries.
134145
if cf.Auths != nil {
135146
for _, form := range domainForms {
136-
if entry, ok := cf.Auths[fmt.Sprintf(form, reg.Name())]; ok {
147+
if entry, ok := cf.Auths[fmt.Sprintf(form, target.RegistryStr())]; ok {
137148
if entry.Auth != "" {
138149
return &auth{entry.Auth}, nil
139150
} else if entry.Username != "" {

pkg/authn/keychain_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,8 @@ func checkHelper(t *testing.T) {
156156
if help.name != "test" {
157157
t.Errorf("Resolve().name; got %v, want \"test\"", help.name)
158158
}
159-
if help.domain != testRegistry {
160-
t.Errorf("Resolve().domain; got %v, want %v", help.domain, testRegistry)
159+
if help.domain != testRegistry.RegistryStr() {
160+
t.Errorf("Resolve().domain; got %v, want %v", help.domain, testRegistry.RegistryStr())
161161
}
162162
}
163163

pkg/authn/multikeychain.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,6 @@
1414

1515
package authn
1616

17-
import (
18-
"github.com/google/go-containerregistry/pkg/name"
19-
)
20-
2117
type multiKeychain struct {
2218
keychains []Keychain
2319
}
@@ -31,9 +27,9 @@ func NewMultiKeychain(kcs ...Keychain) Keychain {
3127
}
3228

3329
// Resolve implements Keychain.
34-
func (mk *multiKeychain) Resolve(reg name.Registry) (Authenticator, error) {
30+
func (mk *multiKeychain) Resolve(target Resource) (Authenticator, error) {
3531
for _, kc := range mk.keychains {
36-
auth, err := kc.Resolve(reg)
32+
auth, err := kc.Resolve(target)
3733
if err != nil {
3834
return nil, err
3935
}

pkg/authn/multikeychain_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,13 @@ func TestMultiKeychain(t *testing.T) {
8585
}
8686
}
8787

88-
type fixedKeychain map[name.Registry]Authenticator
88+
type fixedKeychain map[Resource]Authenticator
8989

9090
var _ Keychain = (fixedKeychain)(nil)
9191

9292
// Resolve implements Keychain.
93-
func (fk fixedKeychain) Resolve(reg name.Registry) (Authenticator, error) {
94-
if auth, ok := fk[reg]; ok {
93+
func (fk fixedKeychain) Resolve(target Resource) (Authenticator, error) {
94+
if auth, ok := fk[target]; ok {
9595
return auth, nil
9696
}
9797
return Anonymous, nil

pkg/v1/google/keychain.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919
"strings"
2020

2121
"github.com/google/go-containerregistry/pkg/authn"
22-
"github.com/google/go-containerregistry/pkg/name"
2322
)
2423

2524
// Keychain exports an instance of the google Keychain.
@@ -48,9 +47,9 @@ type googleKeychain struct{}
4847
//
4948
// In general, we don't worry about that here because we expect to use the same
5049
// gcloud configuration in the scope of this one process.
51-
func (gk *googleKeychain) Resolve(reg name.Registry) (authn.Authenticator, error) {
50+
func (gk *googleKeychain) Resolve(target authn.Resource) (authn.Authenticator, error) {
5251
// Only authenticate GCR so it works with authn.NewMultiKeychain to fallback.
53-
if !strings.HasSuffix(reg.String(), "gcr.io") {
52+
if !strings.HasSuffix(target.RegistryStr(), "gcr.io") {
5453
return authn.Anonymous, nil
5554
}
5655

pkg/v1/remote/delete.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626

2727
// Delete removes the specified image reference from the remote registry.
2828
func Delete(ref name.Reference, options ...Option) error {
29-
o, err := makeOptions(ref.Context().Registry, options...)
29+
o, err := makeOptions(ref.Context(), options...)
3030
if err != nil {
3131
return err
3232
}

pkg/v1/remote/descriptor.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ func Get(ref name.Reference, options ...Option) (*Descriptor, error) {
8585
// Handle options and fetch the manifest with the acceptable MediaTypes in the
8686
// Accept header.
8787
func get(ref name.Reference, acceptable []types.MediaType, options ...Option) (*Descriptor, error) {
88-
o, err := makeOptions(ref.Context().Registry, options...)
88+
o, err := makeOptions(ref.Context(), options...)
8989
if err != nil {
9090
return nil, err
9191
}

pkg/v1/remote/layer.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ func (rl *remoteLayer) MediaType() (types.MediaType, error) {
5858
// digest of the blob to be read and the repository portion is the repo where
5959
// that blob lives.
6060
func Layer(ref name.Digest, options ...Option) (v1.Layer, error) {
61-
o, err := makeOptions(ref.Context().Registry, options...)
61+
o, err := makeOptions(ref.Context(), options...)
6262
if err != nil {
6363
return nil, err
6464
}

pkg/v1/remote/list.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ type tags struct {
3232
// List calls /tags/list for the given repository, returning the list of tags
3333
// in the "tags" property.
3434
func List(repo name.Repository, options ...Option) ([]string, error) {
35-
o, err := makeOptions(repo.Registry, options...)
35+
o, err := makeOptions(repo, options...)
3636
if err != nil {
3737
return nil, err
3838
}

pkg/v1/remote/options.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import (
1919

2020
"github.com/google/go-containerregistry/pkg/authn"
2121
"github.com/google/go-containerregistry/pkg/logs"
22-
"github.com/google/go-containerregistry/pkg/name"
2322
v1 "github.com/google/go-containerregistry/pkg/v1"
2423
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
2524
)
@@ -34,7 +33,7 @@ type options struct {
3433
platform v1.Platform
3534
}
3635

37-
func makeOptions(reg name.Registry, opts ...Option) (*options, error) {
36+
func makeOptions(target authn.Resource, opts ...Option) (*options, error) {
3837
o := &options{
3938
auth: authn.Anonymous,
4039
transport: http.DefaultTransport,
@@ -48,7 +47,7 @@ func makeOptions(reg name.Registry, opts ...Option) (*options, error) {
4847
}
4948

5049
if o.keychain != nil {
51-
auth, err := o.keychain.Resolve(reg)
50+
auth, err := o.keychain.Resolve(target)
5251
if err != nil {
5352
return nil, err
5453
}

pkg/v1/remote/write.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func Write(ref name.Reference, img v1.Image, options ...Option) error {
4646
return err
4747
}
4848

49-
o, err := makeOptions(ref.Context().Registry, options...)
49+
o, err := makeOptions(ref.Context(), options...)
5050
if err != nil {
5151
return err
5252
}
@@ -438,7 +438,7 @@ func WriteIndex(ref name.Reference, ii v1.ImageIndex, options ...Option) error {
438438
return err
439439
}
440440

441-
o, err := makeOptions(ref.Context().Registry, options...)
441+
o, err := makeOptions(ref.Context(), options...)
442442
if err != nil {
443443
return err
444444
}
@@ -494,7 +494,7 @@ func WriteIndex(ref name.Reference, ii v1.ImageIndex, options ...Option) error {
494494

495495
// WriteLayer uploads the provided Layer to the specified name.Digest.
496496
func WriteLayer(ref name.Digest, layer v1.Layer, options ...Option) error {
497-
o, err := makeOptions(ref.Context().Registry, options...)
497+
o, err := makeOptions(ref.Context(), options...)
498498
if err != nil {
499499
return err
500500
}

0 commit comments

Comments
 (0)