Skip to content
This repository has been archived by the owner on Apr 7, 2024. It is now read-only.

feat: support credential function #45

Merged
merged 11 commits into from
Apr 20, 2023
30 changes: 26 additions & 4 deletions registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func Login(ctx context.Context, store Store, reg *remote.Registry, cred auth.Cre
if err := regClone.Ping(ctx); err != nil {
return fmt.Errorf("unable to ping the registry %s: %w", regClone.Reference.Registry, err)
}
hostname := mapHostname(regClone.Reference.Registry)
hostname := mapStoreRegistryName(regClone.Reference.Registry)
if err := store.Put(ctx, hostname, cred); err != nil {
return fmt.Errorf("unable to store the credential for %s: %w", hostname, err)
}
Expand All @@ -57,17 +57,39 @@ func Login(ctx context.Context, store Store, reg *remote.Registry, cred auth.Cre

// Logout provides the logout functionality given the registry name.
func Logout(ctx context.Context, store Store, registryName string) error {
registryName = mapHostname(registryName)
registryName = mapStoreRegistryName(registryName)
if err := store.Delete(ctx, registryName); err != nil {
return fmt.Errorf("unable to delete the credential for %s: %w", registryName, err)
}
return nil
}

func mapHostname(hostname string) string {
// Credential returns a Credential() function that can be used by auth.Client.
func Credential(store Store) func(context.Context, string) (auth.Credential, error) {
return func(ctx context.Context, reg string) (auth.Credential, error) {
reg = mapAuthenticationRegistryName(reg)
if reg == "" {
return auth.EmptyCredential, nil
}
return store.Get(ctx, reg)
}
}

func mapStoreRegistryName(registry string) string {
// The Docker CLI expects that the 'docker.io' credential
// will be added under the key "https://index.docker.io/v1/"
if hostname == "docker.io" {
// See: https://github.com/moby/moby/blob/v24.0.0-beta.2/registry/config.go#L25-L48
if registry == "docker.io" {
return "https://index.docker.io/v1/"
}
return registry
}

func mapAuthenticationRegistryName(hostname string) string {
// It is expected that the traffic targetting "registry-1.docker.io"
// will be redirected to "https://index.docker.io/v1/"
// See: https://github.com/moby/moby/blob/v24.0.0-beta.2/registry/config.go#L25-L48
if hostname == "registry-1.docker.io" {
return "https://index.docker.io/v1/"
}
return hostname
Expand Down
58 changes: 51 additions & 7 deletions registry_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,21 +159,65 @@ func Test_mapHostname(t *testing.T) {
want string
}{
{
"map docker.io to https://index.docker.io/v1/",
"docker.io",
"https://index.docker.io/v1/",
name: "map docker.io to https://index.docker.io/v1/",
host: "docker.io",
want: "https://index.docker.io/v1/",
},
{
"do not map other host names",
"localhost:2333",
"localhost:2333",
name: "do not map other host names",
host: "localhost:2333",
want: "localhost:2333",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := mapHostname(tt.host); got != tt.want {
if got := mapStoreRegistryName(tt.host); got != tt.want {
t.Errorf("mapHostname() = %v, want %v", got, tt.want)
}
})
}
}

func TestCredential(t *testing.T) {
// create a test store
s := &testStore{}
s.storage = map[string]auth.Credential{
"localhost:2333": {Username: "test_user", Password: "test_word"},
"https://index.docker.io/v1/": {Username: "user", Password: "word"},
}
// create a test client using Credential
testClient := &auth.Client{}
testClient.Credential = Credential(s)
tests := []struct {
name string
registry string
wantCredential auth.Credential
}{
{
name: "get credentials for localhost:2333",
registry: "localhost:2333",
wantCredential: auth.Credential{Username: "test_user", Password: "test_word"},
},
{
name: "get credentials for registry-1.docker.io",
registry: "registry-1.docker.io",
wantCredential: auth.Credential{Username: "user", Password: "word"},
},
{
name: "get credentials for a registry not stored",
registry: "localhost:6666",
wantCredential: auth.EmptyCredential,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := testClient.Credential(context.Background(), tt.registry)
if err != nil {
t.Errorf("could not get credential: %v", err)
}
if !reflect.DeepEqual(got, tt.wantCredential) {
t.Errorf("Credential() = %v, want %v", got, tt.wantCredential)
}
})
}
}