cleanup: move packages to top level, change vanity URL

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi
2021-05-25 16:53:26 -07:00
parent e13153a284
commit af776b8abe
45 changed files with 74 additions and 74 deletions

104
solver/fs.go Normal file
View File

@@ -0,0 +1,104 @@
package solver
import (
"context"
"errors"
"io/fs"
"time"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
fstypes "github.com/tonistiigi/fsutil/types"
)
// BuildkitFS is a io/fs.FS adapter for Buildkit
// BuildkitFS implements the ReadFileFS, StatFS and ReadDirFS interfaces.
type BuildkitFS struct {
ref bkgw.Reference
}
func NewBuildkitFS(ref bkgw.Reference) *BuildkitFS {
return &BuildkitFS{
ref: ref,
}
}
// Open is not supported.
func (f *BuildkitFS) Open(name string) (fs.File, error) {
return nil, errors.New("not implemented")
}
func (f *BuildkitFS) Stat(name string) (fs.FileInfo, error) {
st, err := f.ref.StatFile(context.TODO(), bkgw.StatRequest{
Path: name,
})
if err != nil {
return nil, err
}
return bkFileInfo{st}, nil
}
func (f *BuildkitFS) ReadDir(name string) ([]fs.DirEntry, error) {
entries, err := f.ref.ReadDir(context.TODO(), bkgw.ReadDirRequest{
Path: name,
})
if err != nil {
return nil, err
}
res := make([]fs.DirEntry, 0, len(entries))
for _, st := range entries {
res = append(res, bkDirEntry{
bkFileInfo: bkFileInfo{
st: st,
},
})
}
return res, nil
}
func (f *BuildkitFS) ReadFile(name string) ([]byte, error) {
return f.ref.ReadFile(context.TODO(), bkgw.ReadRequest{
Filename: name,
})
}
// bkFileInfo is a fs.FileInfo adapter for fstypes.Stat
type bkFileInfo struct {
st *fstypes.Stat
}
func (s bkFileInfo) Name() string {
return s.st.GetPath()
}
func (s bkFileInfo) Size() int64 {
return s.st.GetSize_()
}
func (s bkFileInfo) IsDir() bool {
return s.st.IsDir()
}
func (s bkFileInfo) ModTime() time.Time {
return time.Unix(s.st.GetModTime(), 0)
}
func (s bkFileInfo) Mode() fs.FileMode {
return fs.FileMode(s.st.Mode)
}
func (s bkFileInfo) Sys() interface{} {
return s.st
}
// bkDirEntry is a fs.DirEntry adapter for fstypes.Stat
type bkDirEntry struct {
bkFileInfo
}
func (s bkDirEntry) Info() (fs.FileInfo, error) {
return s.bkFileInfo, nil
}
func (s bkDirEntry) Type() fs.FileMode {
return s.Mode()
}

86
solver/registryauth.go Normal file
View File

@@ -0,0 +1,86 @@
package solver
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")
}

206
solver/solver.go Normal file
View File

@@ -0,0 +1,206 @@
package solver
import (
"context"
"encoding/json"
"fmt"
bk "github.com/moby/buildkit/client"
"github.com/moby/buildkit/client/llb"
"github.com/moby/buildkit/exporter/containerimage/exptypes"
"github.com/moby/buildkit/frontend/dockerfile/dockerfile2llb"
bkgw "github.com/moby/buildkit/frontend/gateway/client"
"github.com/moby/buildkit/session"
bkpb "github.com/moby/buildkit/solver/pb"
"github.com/opencontainers/go-digest"
"github.com/rs/zerolog/log"
)
type Solver struct {
opts Opts
}
type Opts struct {
Control *bk.Client
Gateway bkgw.Client
Events chan *bk.SolveStatus
Auth *RegistryAuthProvider
NoCache bool
}
func New(opts Opts) Solver {
return Solver{
opts: opts,
}
}
func invalidateCache(def *llb.Definition) error {
for _, dt := range def.Def {
var op bkpb.Op
if err := (&op).Unmarshal(dt); err != nil {
return err
}
dgst := digest.FromBytes(dt)
opMetadata, ok := def.Metadata[dgst]
if !ok {
opMetadata = bkpb.OpMetadata{}
}
c := llb.Constraints{Metadata: opMetadata}
llb.IgnoreCache(&c)
def.Metadata[dgst] = c.Metadata
}
return nil
}
func (s Solver) NoCache() bool {
return s.opts.NoCache
}
func (s Solver) AddCredentials(target, username, secret string) {
s.opts.Auth.AddCredentials(target, username, secret)
}
func (s Solver) Marshal(ctx context.Context, st llb.State) (*bkpb.Definition, error) {
// FIXME: do not hardcode the platform
def, err := st.Marshal(ctx, llb.LinuxAmd64)
if err != nil {
return nil, err
}
if s.opts.NoCache {
if err := invalidateCache(def); err != nil {
return nil, err
}
}
return def.ToPB(), nil
}
func (s Solver) SessionID() string {
return s.opts.Gateway.BuildOpts().SessionID
}
func (s Solver) ResolveImageConfig(ctx context.Context, ref string, opts llb.ResolveImageConfigOpt) (dockerfile2llb.Image, error) {
var image dockerfile2llb.Image
// Load image metadata and convert to to LLB.
// Inspired by https://github.com/moby/buildkit/blob/master/frontend/dockerfile/dockerfile2llb/convert.go
// FIXME: this needs to handle platform
_, meta, err := s.opts.Gateway.ResolveImageConfig(ctx, ref, opts)
if err != nil {
return image, err
}
if err := json.Unmarshal(meta, &image); err != nil {
return image, err
}
return image, nil
}
// Solve will block until the state is solved and returns a Reference.
func (s Solver) SolveRequest(ctx context.Context, req bkgw.SolveRequest) (*bkgw.Result, error) {
return s.opts.Gateway.Solve(ctx, req)
}
// Solve will block until the state is solved and returns a Reference.
func (s Solver) Solve(ctx context.Context, st llb.State) (bkgw.Reference, error) {
// marshal llb
def, err := s.Marshal(ctx, st)
if err != nil {
return nil, err
}
jsonLLB, err := dumpLLB(def)
if err != nil {
return nil, err
}
log.
Ctx(ctx).
Trace().
RawJSON("llb", jsonLLB).
Msg("solving")
// call solve
res, err := s.SolveRequest(ctx, bkgw.SolveRequest{
Definition: def,
// makes Solve() to block until LLB graph is solved. otherwise it will
// return result (that you can for example use for next build) that
// will be evaluated on export or if you access files on it.
Evaluate: true,
})
if err != nil {
return nil, err
}
return res.SingleRef()
}
// Export will export `st` to `output`
// FIXME: this is currently impleneted as a hack, starting a new Build session
// within buildkit from the Control API. Ideally the Gateway API should allow to
// Export directly.
func (s Solver) Export(ctx context.Context, st llb.State, img *dockerfile2llb.Image, output bk.ExportEntry) (*bk.SolveResponse, error) {
def, err := s.Marshal(ctx, st)
if err != nil {
return nil, err
}
opts := bk.SolveOpt{
Exports: []bk.ExportEntry{output},
Session: []session.Attachable{s.opts.Auth},
}
ch := make(chan *bk.SolveStatus)
// Forward this build session events to the main events channel, for logging
// purposes.
go func() {
for event := range ch {
s.opts.Events <- event
}
}()
return s.opts.Control.Build(ctx, opts, "", func(ctx context.Context, c bkgw.Client) (*bkgw.Result, error) {
res, err := c.Solve(ctx, bkgw.SolveRequest{
Definition: def,
})
if err != nil {
return nil, err
}
// Attach the image config if provided
if img != nil {
config, err := json.Marshal(img)
if err != nil {
return nil, fmt.Errorf("failed to marshal image config: %w", err)
}
res.AddMeta(exptypes.ExporterImageConfigKey, config)
}
return res, nil
}, ch)
}
type llbOp struct {
Op bkpb.Op
Digest digest.Digest
OpMetadata bkpb.OpMetadata
}
func dumpLLB(def *bkpb.Definition) ([]byte, error) {
ops := make([]llbOp, 0, len(def.Def))
for _, dt := range def.Def {
var op bkpb.Op
if err := (&op).Unmarshal(dt); err != nil {
return nil, fmt.Errorf("failed to parse op: %w", err)
}
dgst := digest.FromBytes(dt)
ent := llbOp{Op: op, Digest: dgst, OpMetadata: def.Metadata[dgst]}
ops = append(ops, ent)
}
return json.Marshal(ops)
}