Merge pull request #1454 from grouville/registry-parsing
Re-implement docker registry parsing
This commit is contained in:
commit
587f92c924
@ -2,16 +2,18 @@ package solver
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
bkauth "github.com/moby/buildkit/session/auth"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
const defaultDockerDomain = "docker.io"
|
||||
|
||||
// RegistryAuthProvider is a buildkit provider for registry authentication
|
||||
// Adapted from: https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go
|
||||
type RegistryAuthProvider struct {
|
||||
@ -42,7 +44,7 @@ func (a *RegistryAuthProvider) Register(server *grpc.Server) {
|
||||
func (a *RegistryAuthProvider) Credentials(ctx context.Context, req *bkauth.CredentialsRequest) (*bkauth.CredentialsResponse, error) {
|
||||
host := req.Host
|
||||
if host == "registry-1.docker.io" {
|
||||
host = "docker.io"
|
||||
host = defaultDockerDomain
|
||||
}
|
||||
|
||||
a.m.RLock()
|
||||
@ -53,7 +55,6 @@ func (a *RegistryAuthProvider) Credentials(ctx context.Context, req *bkauth.Cred
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if u == host {
|
||||
return auth, nil
|
||||
}
|
||||
@ -62,16 +63,57 @@ func (a *RegistryAuthProvider) Credentials(ctx context.Context, req *bkauth.Cred
|
||||
return &bkauth.CredentialsResponse{}, nil
|
||||
}
|
||||
|
||||
// Parsing function based on splitReposSearchTerm
|
||||
// "github.com/docker/docker/registry"
|
||||
func parseAuthHost(host string) (string, error) {
|
||||
host = strings.TrimPrefix(host, "http://")
|
||||
host = strings.TrimPrefix(host, "https://")
|
||||
host = strings.TrimSuffix(host, "/")
|
||||
|
||||
ref, err := reference.ParseNormalizedNamed(host)
|
||||
// Remove everything after @
|
||||
nameParts := strings.SplitN(host, "@", 2)
|
||||
host = nameParts[0]
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
// if ":" > 1, trim after last ":" found
|
||||
if strings.Count(host, ":") > 1 {
|
||||
host = host[:strings.LastIndex(host, ":")]
|
||||
}
|
||||
return reference.Domain(ref), nil
|
||||
|
||||
// if ":" > 0, trim after last ":" found if it contains "."
|
||||
// ex: samalba/hipache:1.15, registry.com:5000:1.0
|
||||
if strings.Count(host, ":") > 0 {
|
||||
tmpStr := host[strings.LastIndex(host, ":"):]
|
||||
if strings.Count(tmpStr, ".") > 0 {
|
||||
host = host[:strings.LastIndex(host, ":")]
|
||||
}
|
||||
}
|
||||
|
||||
nameParts = strings.SplitN(host, "/", 2)
|
||||
var domain string
|
||||
switch {
|
||||
// Localhost registry parsing
|
||||
case strings.Contains(nameParts[0], "localhost"):
|
||||
domain = nameParts[0]
|
||||
// If the split returned an array of len 1 that doesn't contain any .
|
||||
// ex: ubuntu
|
||||
case len(nameParts) == 1 && !strings.Contains(nameParts[0], "."):
|
||||
domain = defaultDockerDomain
|
||||
// if the split does not contain "." nor ":", but contains images
|
||||
// ex: samalba/hipache, samalba/hipache:1.15, samalba/hipache@sha:...
|
||||
case !strings.Contains(nameParts[0], ".") && !strings.Contains(nameParts[0], ":"):
|
||||
domain = defaultDockerDomain
|
||||
case nameParts[0] == "registry-1.docker.io":
|
||||
domain = defaultDockerDomain
|
||||
case nameParts[0] == "index.docker.io":
|
||||
domain = defaultDockerDomain
|
||||
// Private remaining registry parsing
|
||||
case strings.Contains(nameParts[0], "."):
|
||||
domain = nameParts[0]
|
||||
// Fail by default
|
||||
default:
|
||||
return "", fmt.Errorf("failed parsing [%s] expected host format: [%s]", nameParts[0], "registrydomain.extension")
|
||||
}
|
||||
return domain, nil
|
||||
}
|
||||
|
||||
func (a *RegistryAuthProvider) FetchToken(ctx context.Context, req *bkauth.FetchTokenRequest) (rr *bkauth.FetchTokenResponse, err error) {
|
||||
|
281
solver/registryauth_test.go
Normal file
281
solver/registryauth_test.go
Normal file
@ -0,0 +1,281 @@
|
||||
package solver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseAuthHost(t *testing.T) {
|
||||
type hcase struct {
|
||||
Host, Domain string
|
||||
}
|
||||
|
||||
scases := []hcase{
|
||||
// Short
|
||||
{
|
||||
Host: "foo",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "foo:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "foo@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// Short image
|
||||
{
|
||||
Host: "foo/bar",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "foo/bar:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// Private registry
|
||||
{
|
||||
Host: "registry.com",
|
||||
Domain: "registry.com",
|
||||
},
|
||||
{
|
||||
Host: "registry.com:1.1",
|
||||
Domain: "registry.com",
|
||||
},
|
||||
{
|
||||
Host: "registry.com@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "registry.com",
|
||||
},
|
||||
|
||||
// Private image
|
||||
{
|
||||
Host: "registry.com/foo/bar",
|
||||
Domain: "registry.com",
|
||||
},
|
||||
{
|
||||
Host: "registry.com/foo/bar:1.1",
|
||||
Domain: "registry.com",
|
||||
},
|
||||
{
|
||||
Host: "registry.com/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "registry.com",
|
||||
},
|
||||
|
||||
// Private registry with port
|
||||
{
|
||||
Host: "registry.com:5000",
|
||||
Domain: "registry.com:5000",
|
||||
},
|
||||
{
|
||||
Host: "registry.com:5000:1.1",
|
||||
Domain: "registry.com:5000",
|
||||
},
|
||||
{
|
||||
Host: "registry.com:5000@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "registry.com:5000",
|
||||
},
|
||||
|
||||
// Private image with port
|
||||
{
|
||||
Host: "registry.com:5000/foo/bar",
|
||||
Domain: "registry.com:5000",
|
||||
},
|
||||
{
|
||||
Host: "registry.com:5000/foo/bar:1.1",
|
||||
Domain: "registry.com:5000",
|
||||
},
|
||||
{
|
||||
Host: "registry.com:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "registry.com:5000",
|
||||
},
|
||||
|
||||
// docker.io short
|
||||
{
|
||||
Host: "docker.io",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "docker.io:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "docker.io@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// docker.io image
|
||||
{
|
||||
Host: "docker.io/foo/bar",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "docker.io/foo/bar:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "docker.io/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// registry-1.docker.io short
|
||||
{
|
||||
Host: "registry-1.docker.io",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "registry-1.docker.io:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "registry-1.docker.io@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// registry-1.docker.io image
|
||||
{
|
||||
Host: "registry-1.docker.io/foo/bar",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "registry-1.docker.io/foo/bar:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "registry-1.docker.io/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// index.docker.io short
|
||||
{
|
||||
Host: "index.docker.io",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "index.docker.io:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "index.docker.io@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// index.docker.io image
|
||||
{
|
||||
Host: "index.docker.io/foo/bar",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "index.docker.io/foo/bar:1.1",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "index.docker.io/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
|
||||
// localhost repository
|
||||
{
|
||||
Host: "localhost",
|
||||
Domain: "localhost",
|
||||
},
|
||||
{
|
||||
Host: "localhost:1.1",
|
||||
Domain: "localhost",
|
||||
},
|
||||
{
|
||||
Host: "localhost@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "localhost",
|
||||
},
|
||||
|
||||
// localhost image
|
||||
{
|
||||
Host: "localhost/foo/bar",
|
||||
Domain: "localhost",
|
||||
},
|
||||
{
|
||||
Host: "localhost/foo/bar:1.1",
|
||||
Domain: "localhost",
|
||||
},
|
||||
{
|
||||
Host: "localhost/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "localhost",
|
||||
},
|
||||
|
||||
// localhost repository with port
|
||||
{
|
||||
Host: "localhost:5000",
|
||||
Domain: "localhost:5000",
|
||||
},
|
||||
{
|
||||
Host: "localhost:5000:1.1",
|
||||
Domain: "localhost:5000",
|
||||
},
|
||||
{
|
||||
Host: "localhost:5000@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "localhost:5000",
|
||||
},
|
||||
|
||||
// localhost image with port
|
||||
{
|
||||
Host: "localhost:5000/foo/bar",
|
||||
Domain: "localhost:5000",
|
||||
},
|
||||
{
|
||||
Host: "localhost:5000/foo/bar:1.1",
|
||||
Domain: "localhost:5000",
|
||||
},
|
||||
{
|
||||
Host: "localhost:5000/foo/bar@sha256:bc8813ea7b3603864987522f02a76101c17ad122e1c46d790efc0fca78ca7bfb",
|
||||
Domain: "localhost:5000",
|
||||
},
|
||||
|
||||
// empty host
|
||||
{
|
||||
Host: "",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
{
|
||||
Host: "/jo",
|
||||
Domain: "docker.io",
|
||||
},
|
||||
}
|
||||
|
||||
fcases := []hcase{
|
||||
{
|
||||
Host: ":/jo",
|
||||
},
|
||||
}
|
||||
|
||||
type output struct {
|
||||
expected, actual string
|
||||
}
|
||||
|
||||
successRefs := []output{}
|
||||
for _, scase := range scases {
|
||||
named, err := parseAuthHost(scase.Host)
|
||||
if err != nil {
|
||||
t.Fatalf("Invalid normalized reference for [%q]. Got %q", scase, err)
|
||||
}
|
||||
successRefs = append(successRefs, output{
|
||||
actual: named,
|
||||
expected: scase.Domain,
|
||||
})
|
||||
}
|
||||
for _, r := range successRefs {
|
||||
if r.expected != r.actual {
|
||||
t.Fatalf("Invalid normalized reference for [%q]. Expected %q, got %q", r, r.expected, r.actual)
|
||||
}
|
||||
}
|
||||
|
||||
for _, fcase := range fcases {
|
||||
named, err := parseAuthHost(fcase.Host)
|
||||
if err == nil {
|
||||
t.Fatalf("Invalid normalized reference for [%q]. Expected failure for %q", fcase, named)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user