diff --git a/oci/auth/login/login.go b/oci/auth/login/login.go index ae26ca37..7a72d531 100644 --- a/oci/auth/login/login.go +++ b/oci/auth/login/login.go @@ -18,6 +18,7 @@ package login import ( "context" + "strings" "github.com/google/go-containerregistry/pkg/authn" "github.com/google/go-containerregistry/pkg/name" @@ -30,15 +31,25 @@ import ( // ImageRegistryProvider analyzes the provided registry and returns the identified // container image registry provider. -func ImageRegistryProvider(ref name.Reference) oci.Provider { - _, _, ok := aws.ParseRegistry(ref.Context().RegistryStr()) +func ImageRegistryProvider(url string, ref name.Reference) oci.Provider { + // If the url is a repository root address, use it to analyze. Else, derive + // the registry from the name reference. + // NOTE: This is because name.Reference of a repository root assumes that + // the reference is an image name and defaults to using index.docker.io as + // the registry host. + addr := strings.TrimSuffix(url, "/") + if strings.ContainsRune(addr, '/') { + addr = ref.Context().RegistryStr() + } + + _, _, ok := aws.ParseRegistry(addr) if ok { return oci.ProviderAWS } - if gcp.ValidHost(ref.Context().RegistryStr()) { + if gcp.ValidHost(addr) { return oci.ProviderGCP } - if azure.ValidHost(ref.Context().RegistryStr()) { + if azure.ValidHost(addr) { return oci.ProviderAzure } return oci.ProviderGeneric @@ -94,14 +105,14 @@ func (m *Manager) WithACRClient(c *azure.Client) *Manager { // Login performs authentication against a registry and returns the // authentication material. For generic registry provider, it is no-op. -func (m *Manager) Login(ctx context.Context, image string, ref name.Reference, opts ProviderOptions) (authn.Authenticator, error) { - switch ImageRegistryProvider(ref) { +func (m *Manager) Login(ctx context.Context, url string, ref name.Reference, opts ProviderOptions) (authn.Authenticator, error) { + switch ImageRegistryProvider(url, ref) { case oci.ProviderAWS: - return m.ecr.Login(ctx, opts.AwsAutoLogin, image) + return m.ecr.Login(ctx, opts.AwsAutoLogin, url) case oci.ProviderGCP: - return m.gcr.Login(ctx, opts.GcpAutoLogin, image, ref) + return m.gcr.Login(ctx, opts.GcpAutoLogin, url, ref) case oci.ProviderAzure: - return m.acr.Login(ctx, opts.AzureAutoLogin, image, ref) + return m.acr.Login(ctx, opts.AzureAutoLogin, url, ref) } return nil, nil } diff --git a/oci/auth/login/login_test.go b/oci/auth/login/login_test.go index aac1da5e..b23be1dc 100644 --- a/oci/auth/login/login_test.go +++ b/oci/auth/login/login_test.go @@ -20,6 +20,7 @@ import ( "context" "net/http" "net/http/httptest" + "strings" "testing" awssdk "github.com/aws/aws-sdk-go-v2/aws" @@ -40,18 +41,26 @@ func TestImageRegistryProvider(t *testing.T) { want oci.Provider }{ {"ecr", "012345678901.dkr.ecr.us-east-1.amazonaws.com/foo:v1", oci.ProviderAWS}, + {"ecr-root", "012345678901.dkr.ecr.us-east-1.amazonaws.com", oci.ProviderAWS}, + {"ecr-root with slash", "012345678901.dkr.ecr.us-east-1.amazonaws.com/", oci.ProviderAWS}, {"gcr", "gcr.io/foo/bar:v1", oci.ProviderGCP}, + {"gcr-root", "gcr.io", oci.ProviderGCP}, {"acr", "foo.azurecr.io/bar:v1", oci.ProviderAzure}, + {"acr-root", "foo.azurecr.io", oci.ProviderAzure}, {"docker.io", "foo/bar:v1", oci.ProviderGeneric}, + {"docker.io-root", "docker.io", oci.ProviderGeneric}, + {"library", "alpine", oci.ProviderGeneric}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { g := NewWithT(t) - ref, err := name.ParseReference(tt.image) + // Trim suffix to allow parsing it as reference without modifying + // the given image address. + ref, err := name.ParseReference(strings.TrimSuffix(tt.image, "/")) g.Expect(err).ToNot(HaveOccurred()) - g.Expect(ImageRegistryProvider(ref)).To(Equal(tt.want)) + g.Expect(ImageRegistryProvider(tt.image, ref)).To(Equal(tt.want)) }) } }