implemented docker-login op to add custom registry credentials to the buildkit from a pipeline

Signed-off-by: Sam Alba <sam.alba@gmail.com>
This commit is contained in:
Sam Alba 2021-04-26 17:39:19 -07:00
parent c28199c76e
commit 45ecc32423
4 changed files with 121 additions and 6 deletions

View File

@ -113,7 +113,9 @@ func (c *Client) buildfn(ctx context.Context, deployment *Deployment, fn ClientD
Msg("spawning buildkit job")
resp, err := c.c.Build(ctx, opts, "", func(ctx context.Context, gw bkgw.Client) (*bkgw.Result, error) {
s := NewSolver(c.c, gw, ch, c.noCache)
// buildkit auth provider (registry)
auth := newRegistryAuthProvider()
s := NewSolver(c.c, gw, ch, auth, c.noCache)
lg.Debug().Msg("loading configuration")
if err := deployment.LoadPlan(ctx, s); err != nil {

View File

@ -197,6 +197,8 @@ func (p *Pipeline) doOp(ctx context.Context, op *compiler.Value, st llb.State) (
return p.Exec(ctx, op, st)
case "export":
return p.Export(ctx, op, st)
case "docker-login":
return p.DockerLogin(ctx, op, st)
case "fetch-container":
return p.FetchContainer(ctx, op, st)
case "push-container":
@ -559,6 +561,32 @@ func (p *Pipeline) Load(ctx context.Context, op *compiler.Value, st llb.State) (
return from.State(), nil
}
func (p *Pipeline) DockerLogin(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
username, err := op.Lookup("username").String()
if err != nil {
return st, err
}
secret, err := op.Lookup("secret").String()
if err != nil {
return st, err
}
target, err := op.Lookup("target").String()
if err != nil {
return st, err
}
p.s.auth.AddCredentials(target, username, secret)
log.
Ctx(ctx).
Debug().
Str("target", target).
Msg("docker login to registry")
return st, nil
}
func (p *Pipeline) FetchContainer(ctx context.Context, op *compiler.Value, st llb.State) (llb.State, error) {
rawRef, err := op.Lookup("ref").String()
if err != nil {

86
dagger/registryauth.go Normal file
View File

@ -0,0 +1,86 @@
package dagger
import (
"context"
"net/url"
"strings"
"sync"
bkauth "github.com/moby/buildkit/session/auth"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// 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 {
credentials map[string]*bkauth.CredentialsResponse
m sync.RWMutex
}
func newRegistryAuthProvider() *registryAuthProvider {
return &registryAuthProvider{
credentials: map[string]*bkauth.CredentialsResponse{},
}
}
func (a *registryAuthProvider) AddCredentials(target, username, secret string) {
a.m.Lock()
defer a.m.Unlock()
a.credentials[target] = &bkauth.CredentialsResponse{
Username: username,
Secret: secret,
}
}
func (a *registryAuthProvider) Register(server *grpc.Server) {
bkauth.RegisterAuthServer(server, a)
}
func (a *registryAuthProvider) Credentials(ctx context.Context, req *bkauth.CredentialsRequest) (*bkauth.CredentialsResponse, error) {
reqURL, err := parseAuthHost(req.Host)
if err != nil {
return nil, err
}
a.m.RLock()
defer a.m.RUnlock()
for authHost, auth := range a.credentials {
u, err := parseAuthHost(authHost)
if err != nil {
return nil, err
}
if u.Host == reqURL.Host {
return auth, nil
}
}
return &bkauth.CredentialsResponse{}, nil
}
func parseAuthHost(host string) (*url.URL, error) {
if host == "registry-1.docker.io" {
host = "https://index.docker.io/v1/"
}
if !strings.HasPrefix(host, "http://") && !strings.HasPrefix(host, "https://") {
host = "https://" + host
}
return url.Parse(host)
}
func (a *registryAuthProvider) FetchToken(ctx context.Context, req *bkauth.FetchTokenRequest) (rr *bkauth.FetchTokenResponse, err error) {
return nil, status.Errorf(codes.Unavailable, "client side tokens not implemented")
}
func (a *registryAuthProvider) GetTokenAuthority(ctx context.Context, req *bkauth.GetTokenAuthorityRequest) (*bkauth.GetTokenAuthorityResponse, error) {
return nil, status.Errorf(codes.Unavailable, "client side tokens not implemented")
}
func (a *registryAuthProvider) VerifyTokenAuthority(ctx context.Context, req *bkauth.VerifyTokenAuthorityRequest) (*bkauth.VerifyTokenAuthorityResponse, error) {
return nil, status.Errorf(codes.Unavailable, "client side tokens not implemented")
}

View File

@ -13,7 +13,6 @@ import (
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
"github.com/moby/buildkit/session/auth/authprovider"
bkpb "github.com/moby/buildkit/solver/pb"
"github.com/opencontainers/go-digest"
"github.com/rs/zerolog/log"
@ -23,14 +22,16 @@ type Solver struct {
events chan *bk.SolveStatus
control *bk.Client
gw bkgw.Client
auth *registryAuthProvider
noCache bool
}
func NewSolver(control *bk.Client, gw bkgw.Client, events chan *bk.SolveStatus, noCache bool) Solver {
func NewSolver(control *bk.Client, gw bkgw.Client, events chan *bk.SolveStatus, auth *registryAuthProvider, noCache bool) Solver {
return Solver{
events: events,
control: control,
gw: gw,
auth: auth,
noCache: noCache,
}
}
@ -148,9 +149,7 @@ func (s Solver) Export(ctx context.Context, st llb.State, img *dockerfile2llb.Im
opts := bk.SolveOpt{
Exports: []bk.ExportEntry{output},
Session: []session.Attachable{
authprovider.NewDockerAuthProvider(log.Ctx(ctx)),
},
Session: []session.Attachable{s.auth},
}
ch := make(chan *bk.SolveStatus)