mirror of
https://github.com/moby/buildkit.git
synced 2026-08-09 01:01:08 +00:00
Support importing git sources from OCI or registry-backed bundle blobs and exporting resolved checkouts as single-file git bundles. Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
221 lines
4.4 KiB
Go
221 lines
4.4 KiB
Go
package containerblob
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"time"
|
|
|
|
cerrdefs "github.com/containerd/errdefs"
|
|
"github.com/moby/buildkit/cache"
|
|
"github.com/moby/buildkit/session"
|
|
"github.com/moby/buildkit/snapshot"
|
|
"github.com/moby/buildkit/solver"
|
|
"github.com/moby/buildkit/source/containerblob/blobfetch"
|
|
"github.com/moby/buildkit/source/util/pathutil"
|
|
"github.com/moby/buildkit/util/contentutil"
|
|
digest "github.com/opencontainers/go-digest"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type puller struct {
|
|
src *Source
|
|
id *ImageBlobIdentifier
|
|
|
|
SessionManager *session.Manager
|
|
|
|
rc io.ReadCloser
|
|
dgst digest.Digest
|
|
}
|
|
|
|
func (p *puller) hash() (digest.Digest, error) {
|
|
dgst := p.id.Reference.Digest()
|
|
if err := dgst.Validate(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
dt, err := json.Marshal(struct {
|
|
Digest digest.Digest
|
|
Filename string
|
|
Perm, UID, GID int
|
|
}{
|
|
Digest: dgst,
|
|
Filename: p.id.Filename,
|
|
Perm: p.id.Perm,
|
|
UID: p.id.UID,
|
|
GID: p.id.GID,
|
|
})
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return digest.FromBytes(dt), nil
|
|
}
|
|
|
|
func (p *puller) ensureResolver(ctx context.Context, g session.Group) error {
|
|
if p.rc != nil {
|
|
return nil
|
|
}
|
|
dgst := p.id.Reference.Digest()
|
|
if err := dgst.Validate(); err != nil {
|
|
return errors.Wrap(err, "invalid reference digest")
|
|
}
|
|
|
|
rc, _, err := blobfetch.FetchBlob(ctx, g, blobfetch.FetchOpt{
|
|
Scheme: p.id.Scheme(),
|
|
Ref: p.id.Reference.String(),
|
|
Digest: dgst,
|
|
RegistryHosts: p.src.RegistryHosts,
|
|
SessionManager: p.SessionManager,
|
|
SessionID: p.id.SessionID,
|
|
StoreID: p.id.StoreID,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
p.rc = rc
|
|
p.dgst = dgst
|
|
return nil
|
|
}
|
|
|
|
func (p *puller) CacheKey(ctx context.Context, jobCtx solver.JobContext, index int) (cacheKey string, imgDigest string, cacheOpts solver.CacheOpts, cacheDone bool, err error) {
|
|
dgst := p.id.Reference.Digest()
|
|
if err := dgst.Validate(); err != nil {
|
|
return "", "", nil, false, errors.Wrap(err, "invalid reference digest")
|
|
}
|
|
|
|
info, err := p.src.ContentStore.Info(ctx, dgst)
|
|
if err != nil {
|
|
if !cerrdefs.IsNotFound(err) {
|
|
return "", "", nil, false, err
|
|
}
|
|
}
|
|
|
|
if ok, err := contentutil.HasSource(info, p.id.Reference); err == nil && ok {
|
|
h, err := p.hash()
|
|
if err != nil {
|
|
return "", "", nil, false, err
|
|
}
|
|
return h.String(), dgst.String(), nil, true, nil
|
|
}
|
|
|
|
h, err := p.hash()
|
|
if err != nil {
|
|
return "", "", nil, false, err
|
|
}
|
|
return h.String(), dgst.String(), nil, true, nil
|
|
}
|
|
|
|
func (p *puller) Snapshot(ctx context.Context, jobCtx solver.JobContext) (ir cache.ImmutableRef, err error) {
|
|
var g session.Group
|
|
if jobCtx != nil {
|
|
g = jobCtx.Session()
|
|
}
|
|
|
|
if err := p.ensureResolver(ctx, g); err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if p.rc != nil {
|
|
p.rc.Close()
|
|
p.rc = nil
|
|
}
|
|
}()
|
|
|
|
newRef, err := p.src.CacheAccessor.New(ctx, nil, g, cache.CachePolicyRetain, cache.WithDescription(fmt.Sprintf("blob %s", p.id.Reference.String())))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
defer func() {
|
|
if err != nil && newRef != nil {
|
|
newRef.Release(context.WithoutCancel(ctx))
|
|
}
|
|
}()
|
|
|
|
mount, err := newRef.Mount(ctx, false, g)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lm := snapshot.LocalMounter(mount)
|
|
dir, err := lm.Mount()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if err != nil && lm != nil {
|
|
lm.Unmount()
|
|
}
|
|
}()
|
|
|
|
perm := 0600
|
|
if p.id.Perm != 0 {
|
|
perm = p.id.Perm
|
|
}
|
|
fn := p.id.Filename
|
|
if fn == "" {
|
|
fn = p.dgst.Hex()
|
|
} else {
|
|
fn = pathutil.SafeFileName(fn)
|
|
}
|
|
|
|
root, err := os.OpenRoot(dir)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer root.Close()
|
|
|
|
f, err := root.OpenFile(fn, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(perm))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() {
|
|
if f != nil {
|
|
f.Close()
|
|
}
|
|
}()
|
|
|
|
h := sha256.New()
|
|
if _, err := io.Copy(io.MultiWriter(f, h), p.rc); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if err := f.Close(); err != nil {
|
|
return nil, err
|
|
}
|
|
f = nil
|
|
|
|
uid := p.id.UID
|
|
gid := p.id.GID
|
|
if idmap := mount.IdentityMapping(); idmap != nil {
|
|
uid, gid, err = idmap.ToHost(uid, gid)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
if gid != 0 || uid != 0 {
|
|
if err := root.Chown(fn, uid, gid); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
mTime := time.Unix(0, 0)
|
|
if err := root.Chtimes(fn, mTime, mTime); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
lm.Unmount()
|
|
lm = nil
|
|
|
|
ref, err := newRef.Commit(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
newRef = nil
|
|
return ref, nil
|
|
}
|