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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/distribution/reference"
|
|
||||||
bkauth "github.com/moby/buildkit/session/auth"
|
bkauth "github.com/moby/buildkit/session/auth"
|
||||||
"google.golang.org/grpc"
|
"google.golang.org/grpc"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultDockerDomain = "docker.io"
|
||||||
|
|
||||||
// RegistryAuthProvider is a buildkit provider for registry authentication
|
// RegistryAuthProvider is a buildkit provider for registry authentication
|
||||||
// Adapted from: https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go
|
// Adapted from: https://github.com/moby/buildkit/blob/master/session/auth/authprovider/authprovider.go
|
||||||
type RegistryAuthProvider struct {
|
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) {
|
func (a *RegistryAuthProvider) Credentials(ctx context.Context, req *bkauth.CredentialsRequest) (*bkauth.CredentialsResponse, error) {
|
||||||
host := req.Host
|
host := req.Host
|
||||||
if host == "registry-1.docker.io" {
|
if host == "registry-1.docker.io" {
|
||||||
host = "docker.io"
|
host = defaultDockerDomain
|
||||||
}
|
}
|
||||||
|
|
||||||
a.m.RLock()
|
a.m.RLock()
|
||||||
@ -53,7 +55,6 @@ func (a *RegistryAuthProvider) Credentials(ctx context.Context, req *bkauth.Cred
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if u == host {
|
if u == host {
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
@ -62,16 +63,57 @@ func (a *RegistryAuthProvider) Credentials(ctx context.Context, req *bkauth.Cred
|
|||||||
return &bkauth.CredentialsResponse{}, nil
|
return &bkauth.CredentialsResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Parsing function based on splitReposSearchTerm
|
||||||
|
// "github.com/docker/docker/registry"
|
||||||
func parseAuthHost(host string) (string, error) {
|
func parseAuthHost(host string) (string, error) {
|
||||||
host = strings.TrimPrefix(host, "http://")
|
host = strings.TrimPrefix(host, "http://")
|
||||||
host = strings.TrimPrefix(host, "https://")
|
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 {
|
// if ":" > 1, trim after last ":" found
|
||||||
return "", err
|
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) {
|
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