2017-12-15 08:06:54 +00:00
|
|
|
package base
|
|
|
|
|
|
|
|
import (
|
2018-01-16 22:30:10 +00:00
|
|
|
"context"
|
2018-04-13 21:13:48 +00:00
|
|
|
"fmt"
|
2017-12-15 08:06:54 +00:00
|
|
|
"io"
|
2017-12-19 09:34:34 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2019-07-25 21:13:13 +00:00
|
|
|
"strings"
|
2018-04-13 21:13:48 +00:00
|
|
|
"time"
|
2017-12-15 08:06:54 +00:00
|
|
|
|
|
|
|
"github.com/containerd/containerd/content"
|
|
|
|
"github.com/containerd/containerd/diff"
|
2019-09-18 00:18:32 +00:00
|
|
|
"github.com/containerd/containerd/gc"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/containerd/containerd/images"
|
2019-05-21 18:32:21 +00:00
|
|
|
"github.com/containerd/containerd/leases"
|
2019-08-15 00:00:52 +00:00
|
|
|
"github.com/containerd/containerd/remotes/docker"
|
2019-03-20 06:40:24 +00:00
|
|
|
"github.com/docker/docker/pkg/idtools"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/moby/buildkit/cache"
|
2018-04-13 21:13:48 +00:00
|
|
|
"github.com/moby/buildkit/cache/blobs"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/moby/buildkit/cache/metadata"
|
|
|
|
"github.com/moby/buildkit/client"
|
|
|
|
"github.com/moby/buildkit/executor"
|
|
|
|
"github.com/moby/buildkit/exporter"
|
|
|
|
imageexporter "github.com/moby/buildkit/exporter/containerimage"
|
|
|
|
localexporter "github.com/moby/buildkit/exporter/local"
|
|
|
|
ociexporter "github.com/moby/buildkit/exporter/oci"
|
2019-03-27 06:10:21 +00:00
|
|
|
tarexporter "github.com/moby/buildkit/exporter/tar"
|
2018-04-13 21:13:48 +00:00
|
|
|
"github.com/moby/buildkit/frontend"
|
2018-07-25 06:18:21 +00:00
|
|
|
gw "github.com/moby/buildkit/frontend/gateway/client"
|
2017-12-19 09:34:34 +00:00
|
|
|
"github.com/moby/buildkit/identity"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/moby/buildkit/session"
|
2017-12-28 07:07:13 +00:00
|
|
|
"github.com/moby/buildkit/snapshot"
|
2018-07-27 00:53:48 +00:00
|
|
|
"github.com/moby/buildkit/snapshot/imagerefchecker"
|
2018-05-11 05:58:41 +00:00
|
|
|
"github.com/moby/buildkit/solver"
|
|
|
|
"github.com/moby/buildkit/solver/llbsolver/ops"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/moby/buildkit/solver/pb"
|
|
|
|
"github.com/moby/buildkit/source"
|
|
|
|
"github.com/moby/buildkit/source/containerimage"
|
|
|
|
"github.com/moby/buildkit/source/git"
|
|
|
|
"github.com/moby/buildkit/source/http"
|
|
|
|
"github.com/moby/buildkit/source/local"
|
2018-04-13 21:13:48 +00:00
|
|
|
"github.com/moby/buildkit/util/contentutil"
|
2019-05-21 18:42:48 +00:00
|
|
|
"github.com/moby/buildkit/util/leaseutil"
|
2018-04-13 21:13:48 +00:00
|
|
|
"github.com/moby/buildkit/util/progress"
|
2018-09-07 20:45:59 +00:00
|
|
|
"github.com/moby/buildkit/util/resolver"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/moby/buildkit/worker"
|
|
|
|
digest "github.com/opencontainers/go-digest"
|
2018-04-13 21:13:48 +00:00
|
|
|
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
|
2018-06-22 02:06:12 +00:00
|
|
|
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
2017-12-15 08:06:54 +00:00
|
|
|
"github.com/pkg/errors"
|
2018-11-28 09:43:43 +00:00
|
|
|
"github.com/sirupsen/logrus"
|
2019-07-25 21:13:13 +00:00
|
|
|
bolt "go.etcd.io/bbolt"
|
2018-04-13 21:13:48 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2017-12-15 08:06:54 +00:00
|
|
|
)
|
|
|
|
|
2018-05-05 05:18:11 +00:00
|
|
|
const labelCreatedAt = "buildkit/createdat"
|
|
|
|
|
2017-12-15 08:06:54 +00:00
|
|
|
// TODO: this file should be removed. containerd defines ContainerdWorker, oci defines OCIWorker. There is no base worker.
|
|
|
|
|
|
|
|
// WorkerOpt is specific to a worker.
|
|
|
|
// See also CommonOpt.
|
|
|
|
type WorkerOpt struct {
|
2018-09-07 20:45:59 +00:00
|
|
|
ID string
|
|
|
|
Labels map[string]string
|
|
|
|
Platforms []specs.Platform
|
|
|
|
GCPolicy []client.PruneInfo
|
|
|
|
MetadataStore *metadata.Store
|
|
|
|
Executor executor.Executor
|
|
|
|
Snapshotter snapshot.Snapshotter
|
|
|
|
ContentStore content.Store
|
|
|
|
Applier diff.Applier
|
|
|
|
Differ diff.Comparer
|
|
|
|
ImageStore images.Store // optional
|
|
|
|
ResolveOptionsFunc resolver.ResolveOptionsFunc
|
2019-03-20 06:40:24 +00:00
|
|
|
IdentityMapping *idtools.IdentityMapping
|
2019-05-21 18:32:21 +00:00
|
|
|
LeaseManager leases.Manager
|
2019-09-18 00:18:32 +00:00
|
|
|
GarbageCollect func(context.Context) (gc.Stats, error)
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Worker is a local worker instance with dedicated snapshotter, cache, and so on.
|
|
|
|
// TODO: s/Worker/OpWorker/g ?
|
|
|
|
type Worker struct {
|
|
|
|
WorkerOpt
|
|
|
|
CacheManager cache.Manager
|
|
|
|
SourceManager *source.Manager
|
2019-02-23 12:56:04 +00:00
|
|
|
imageWriter *imageexporter.ImageWriter
|
2017-12-15 08:06:54 +00:00
|
|
|
ImageSource source.Source
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewWorker instantiates a local worker
|
|
|
|
func NewWorker(opt WorkerOpt) (*Worker, error) {
|
2018-07-27 00:53:48 +00:00
|
|
|
imageRefChecker := imagerefchecker.New(imagerefchecker.Opt{
|
|
|
|
ImageStore: opt.ImageStore,
|
|
|
|
ContentStore: opt.ContentStore,
|
|
|
|
})
|
|
|
|
|
2017-12-15 08:06:54 +00:00
|
|
|
cm, err := cache.NewManager(cache.ManagerOpt{
|
2018-07-27 00:53:48 +00:00
|
|
|
Snapshotter: opt.Snapshotter,
|
|
|
|
MetadataStore: opt.MetadataStore,
|
|
|
|
PruneRefChecker: imageRefChecker,
|
2019-09-18 00:18:32 +00:00
|
|
|
Applier: opt.Applier,
|
|
|
|
GarbageCollect: opt.GarbageCollect,
|
|
|
|
LeaseManager: opt.LeaseManager,
|
|
|
|
ContentStore: opt.ContentStore,
|
2017-12-15 08:06:54 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sm, err := source.NewManager()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
is, err := containerimage.NewSource(containerimage.SourceOpt{
|
2019-02-23 12:56:04 +00:00
|
|
|
Snapshotter: opt.Snapshotter,
|
|
|
|
ContentStore: opt.ContentStore,
|
|
|
|
Applier: opt.Applier,
|
|
|
|
ImageStore: opt.ImageStore,
|
|
|
|
CacheAccessor: cm,
|
|
|
|
ResolverOpt: opt.ResolveOptionsFunc,
|
2019-05-21 18:32:21 +00:00
|
|
|
LeaseManager: opt.LeaseManager,
|
2017-12-15 08:06:54 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sm.Register(is)
|
|
|
|
|
2018-11-28 09:43:43 +00:00
|
|
|
if err := git.Supported(); err == nil {
|
|
|
|
gs, err := git.NewSource(git.Opt{
|
|
|
|
CacheAccessor: cm,
|
|
|
|
MetadataStore: opt.MetadataStore,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sm.Register(gs)
|
|
|
|
} else {
|
|
|
|
logrus.Warnf("git source cannot be enabled: %v", err)
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
hs, err := http.NewSource(http.Opt{
|
|
|
|
CacheAccessor: cm,
|
|
|
|
MetadataStore: opt.MetadataStore,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
sm.Register(hs)
|
|
|
|
|
|
|
|
ss, err := local.NewSource(local.Opt{
|
2019-02-23 12:56:04 +00:00
|
|
|
CacheAccessor: cm,
|
|
|
|
MetadataStore: opt.MetadataStore,
|
2017-12-15 08:06:54 +00:00
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
sm.Register(ss)
|
|
|
|
|
|
|
|
iw, err := imageexporter.NewImageWriter(imageexporter.WriterOpt{
|
2017-12-28 07:07:13 +00:00
|
|
|
Snapshotter: opt.Snapshotter,
|
2017-12-15 08:06:54 +00:00
|
|
|
ContentStore: opt.ContentStore,
|
2019-03-28 09:26:34 +00:00
|
|
|
Applier: opt.Applier,
|
2017-12-15 08:06:54 +00:00
|
|
|
Differ: opt.Differ,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2019-09-19 20:59:35 +00:00
|
|
|
leases, err := opt.LeaseManager.List(context.TODO(), "labels.\"buildkit/lease.temporary\"")
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
for _, l := range leases {
|
|
|
|
opt.LeaseManager.Delete(context.TODO(), l)
|
|
|
|
}
|
|
|
|
|
2017-12-15 08:06:54 +00:00
|
|
|
return &Worker{
|
|
|
|
WorkerOpt: opt,
|
|
|
|
CacheManager: cm,
|
|
|
|
SourceManager: sm,
|
2019-02-23 12:56:04 +00:00
|
|
|
imageWriter: iw,
|
2017-12-15 08:06:54 +00:00
|
|
|
ImageSource: is,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-08-15 00:00:52 +00:00
|
|
|
func (w *Worker) ContentStore() content.Store {
|
|
|
|
return w.WorkerOpt.ContentStore
|
|
|
|
}
|
|
|
|
|
2017-12-19 09:34:34 +00:00
|
|
|
func (w *Worker) ID() string {
|
|
|
|
return w.WorkerOpt.ID
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Worker) Labels() map[string]string {
|
|
|
|
return w.WorkerOpt.Labels
|
|
|
|
}
|
|
|
|
|
2018-06-22 02:06:12 +00:00
|
|
|
func (w *Worker) Platforms() []specs.Platform {
|
|
|
|
return w.WorkerOpt.Platforms
|
|
|
|
}
|
|
|
|
|
2018-08-30 21:06:27 +00:00
|
|
|
func (w *Worker) GCPolicy() []client.PruneInfo {
|
|
|
|
return w.WorkerOpt.GCPolicy
|
|
|
|
}
|
|
|
|
|
2018-09-14 20:35:41 +00:00
|
|
|
func (w *Worker) LoadRef(id string, hidden bool) (cache.ImmutableRef, error) {
|
|
|
|
var opts []cache.RefOption
|
|
|
|
if hidden {
|
|
|
|
opts = append(opts, cache.NoUpdateLastUsed)
|
|
|
|
}
|
|
|
|
return w.CacheManager.Get(context.TODO(), id, opts...)
|
2018-04-13 21:13:48 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 12:56:04 +00:00
|
|
|
func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge, sm *session.Manager) (solver.Op, error) {
|
2018-06-24 05:52:19 +00:00
|
|
|
if baseOp, ok := v.Sys().(*pb.Op); ok {
|
|
|
|
switch op := baseOp.Op.(type) {
|
2018-06-22 23:07:13 +00:00
|
|
|
case *pb.Op_Source:
|
2019-02-23 12:56:04 +00:00
|
|
|
return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, sm, w)
|
2018-06-22 23:07:13 +00:00
|
|
|
case *pb.Op_Exec:
|
2019-02-27 22:53:52 +00:00
|
|
|
return ops.NewExecOp(v, op, baseOp.Platform, w.CacheManager, sm, w.MetadataStore, w.Executor, w)
|
2019-02-27 22:40:45 +00:00
|
|
|
case *pb.Op_File:
|
|
|
|
return ops.NewFileOp(v, op, w.CacheManager, w.MetadataStore, w)
|
2018-06-22 23:07:13 +00:00
|
|
|
case *pb.Op_Build:
|
|
|
|
return ops.NewBuildOp(v, op, s, w)
|
2019-01-23 21:05:59 +00:00
|
|
|
default:
|
|
|
|
return nil, errors.Errorf("no support for %T", op)
|
2018-06-22 23:07:13 +00:00
|
|
|
}
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
2018-06-22 23:07:13 +00:00
|
|
|
return nil, errors.Errorf("could not resolve %v", v)
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
|
|
|
|
2019-07-29 22:41:49 +00:00
|
|
|
func (w *Worker) PruneCacheMounts(ctx context.Context, ids []string) error {
|
2019-07-25 21:13:13 +00:00
|
|
|
mu := ops.CacheMountsLocker()
|
|
|
|
mu.Lock()
|
|
|
|
defer mu.Unlock()
|
|
|
|
|
|
|
|
for _, id := range ids {
|
|
|
|
id = "cache-dir:" + id
|
|
|
|
sis, err := w.MetadataStore.Search(id)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
for _, si := range sis {
|
|
|
|
for _, k := range si.Indexes() {
|
|
|
|
if k == id || strings.HasPrefix(k, id+":") {
|
2019-07-29 22:41:49 +00:00
|
|
|
if siCached := w.CacheManager.Metadata(si.ID()); siCached != nil {
|
|
|
|
si = siCached
|
|
|
|
}
|
2019-07-25 21:13:13 +00:00
|
|
|
if err := cache.CachePolicyDefault(si); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
si.Queue(func(b *bolt.Bucket) error {
|
|
|
|
return si.SetValue(b, k, nil)
|
|
|
|
})
|
|
|
|
if err := si.Commit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-07-29 22:41:49 +00:00
|
|
|
// if ref is unused try to clean it up right away by releasing it
|
|
|
|
if mref, err := w.CacheManager.GetMutable(ctx, si.ID()); err == nil {
|
|
|
|
go mref.Release(context.TODO())
|
|
|
|
}
|
2019-07-25 21:13:13 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ops.ClearActiveCacheMounts()
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2019-02-23 12:56:04 +00:00
|
|
|
func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt, sm *session.Manager) (digest.Digest, []byte, error) {
|
2017-12-15 08:06:54 +00:00
|
|
|
// ImageSource is typically source/containerimage
|
|
|
|
resolveImageConfig, ok := w.ImageSource.(resolveImageConfig)
|
|
|
|
if !ok {
|
2017-12-19 09:34:34 +00:00
|
|
|
return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID())
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
2019-02-23 12:56:04 +00:00
|
|
|
return resolveImageConfig.ResolveImageConfig(ctx, ref, opt, sm)
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type resolveImageConfig interface {
|
2019-02-23 12:56:04 +00:00
|
|
|
ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt, sm *session.Manager) (digest.Digest, []byte, error)
|
2017-12-15 08:06:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Worker) Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
|
|
|
|
active, err := w.CacheManager.New(ctx, rootFS)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
defer active.Release(context.TODO())
|
|
|
|
return w.Executor.Exec(ctx, meta, active, nil, stdin, stdout, stderr)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (w *Worker) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) {
|
|
|
|
return w.CacheManager.DiskUsage(ctx, opt)
|
|
|
|
}
|
|
|
|
|
2018-08-30 21:06:27 +00:00
|
|
|
func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, opt ...client.PruneInfo) error {
|
|
|
|
return w.CacheManager.Prune(ctx, ch, opt...)
|
2017-12-27 01:22:50 +00:00
|
|
|
}
|
|
|
|
|
2019-02-23 12:56:04 +00:00
|
|
|
func (w *Worker) Exporter(name string, sm *session.Manager) (exporter.Exporter, error) {
|
|
|
|
switch name {
|
|
|
|
case client.ExporterImage:
|
|
|
|
return imageexporter.New(imageexporter.Opt{
|
|
|
|
Images: w.ImageStore,
|
|
|
|
SessionManager: sm,
|
|
|
|
ImageWriter: w.imageWriter,
|
|
|
|
ResolverOpt: w.ResolveOptionsFunc,
|
2019-07-12 18:50:50 +00:00
|
|
|
LeaseManager: w.LeaseManager,
|
2019-02-23 12:56:04 +00:00
|
|
|
})
|
|
|
|
case client.ExporterLocal:
|
|
|
|
return localexporter.New(localexporter.Opt{
|
|
|
|
SessionManager: sm,
|
|
|
|
})
|
2019-03-27 06:10:21 +00:00
|
|
|
case client.ExporterTar:
|
|
|
|
return tarexporter.New(tarexporter.Opt{
|
|
|
|
SessionManager: sm,
|
|
|
|
})
|
2019-02-23 12:56:04 +00:00
|
|
|
case client.ExporterOCI:
|
|
|
|
return ociexporter.New(ociexporter.Opt{
|
|
|
|
SessionManager: sm,
|
|
|
|
ImageWriter: w.imageWriter,
|
|
|
|
Variant: ociexporter.VariantOCI,
|
2019-07-12 18:50:50 +00:00
|
|
|
LeaseManager: w.LeaseManager,
|
2019-02-23 12:56:04 +00:00
|
|
|
})
|
|
|
|
case client.ExporterDocker:
|
|
|
|
return ociexporter.New(ociexporter.Opt{
|
|
|
|
SessionManager: sm,
|
|
|
|
ImageWriter: w.imageWriter,
|
|
|
|
Variant: ociexporter.VariantDocker,
|
2019-07-12 18:50:50 +00:00
|
|
|
LeaseManager: w.LeaseManager,
|
2019-02-23 12:56:04 +00:00
|
|
|
})
|
|
|
|
default:
|
2017-12-15 08:06:54 +00:00
|
|
|
return nil, errors.Errorf("exporter %q could not be found", name)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-08 21:05:08 +00:00
|
|
|
func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error) {
|
2019-10-03 21:11:54 +00:00
|
|
|
ctx, done, err := leaseutil.WithLease(ctx, w.LeaseManager, leaseutil.MakeTemporary)
|
2019-09-24 22:06:55 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer done(ctx)
|
|
|
|
|
2019-09-18 00:18:32 +00:00
|
|
|
diffPairs, err := blobs.GetDiffPairs(ctx, w.ContentStore(), w.Differ, ref, createIfNeeded)
|
2018-04-13 21:13:48 +00:00
|
|
|
if err != nil {
|
2018-09-21 20:56:05 +00:00
|
|
|
return nil, errors.Wrap(err, "failed calculating diff pairs for exported snapshot")
|
2018-04-13 21:13:48 +00:00
|
|
|
}
|
|
|
|
if len(diffPairs) == 0 {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2018-05-05 05:18:11 +00:00
|
|
|
createdTimes := getCreatedTimes(ref)
|
|
|
|
if len(createdTimes) != len(diffPairs) {
|
|
|
|
return nil, errors.Errorf("invalid createdtimes/diffpairs")
|
|
|
|
}
|
|
|
|
|
2018-04-13 21:13:48 +00:00
|
|
|
descs := make([]ocispec.Descriptor, len(diffPairs))
|
|
|
|
|
|
|
|
for i, dp := range diffPairs {
|
2019-08-15 00:00:52 +00:00
|
|
|
info, err := w.ContentStore().Info(ctx, dp.Blobsum)
|
2018-04-13 21:13:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-05-05 05:18:11 +00:00
|
|
|
|
|
|
|
tm, err := createdTimes[i].MarshalText()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-04-13 21:13:48 +00:00
|
|
|
descs[i] = ocispec.Descriptor{
|
|
|
|
Digest: dp.Blobsum,
|
|
|
|
Size: info.Size,
|
2018-06-26 22:24:33 +00:00
|
|
|
MediaType: images.MediaTypeDockerSchema2LayerGzip,
|
2018-04-13 21:13:48 +00:00
|
|
|
Annotations: map[string]string{
|
|
|
|
"containerd.io/uncompressed": dp.DiffID.String(),
|
2018-05-05 05:18:11 +00:00
|
|
|
labelCreatedAt: string(tm),
|
2018-04-13 21:13:48 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return &solver.Remote{
|
|
|
|
Descriptors: descs,
|
2019-08-15 00:00:52 +00:00
|
|
|
Provider: w.ContentStore(),
|
2018-04-13 21:13:48 +00:00
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2018-05-05 05:18:11 +00:00
|
|
|
func getCreatedTimes(ref cache.ImmutableRef) (out []time.Time) {
|
|
|
|
parent := ref.Parent()
|
|
|
|
if parent != nil {
|
|
|
|
defer parent.Release(context.TODO())
|
|
|
|
out = getCreatedTimes(parent)
|
|
|
|
}
|
|
|
|
return append(out, cache.GetCreatedAt(ref.Metadata()))
|
|
|
|
}
|
|
|
|
|
2019-09-18 00:18:32 +00:00
|
|
|
func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (ref cache.ImmutableRef, err error) {
|
2019-10-03 21:11:54 +00:00
|
|
|
ctx, done, err := leaseutil.WithLease(ctx, w.LeaseManager, leaseutil.MakeTemporary)
|
2019-05-21 18:42:48 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer done(ctx)
|
|
|
|
|
2018-04-13 21:13:48 +00:00
|
|
|
eg, gctx := errgroup.WithContext(ctx)
|
|
|
|
for _, desc := range remote.Descriptors {
|
|
|
|
func(desc ocispec.Descriptor) {
|
|
|
|
eg.Go(func() error {
|
|
|
|
done := oneOffProgress(ctx, fmt.Sprintf("pulling %s", desc.Digest))
|
2019-08-15 00:00:52 +00:00
|
|
|
if err := contentutil.Copy(gctx, w.ContentStore(), remote.Provider, desc); err != nil {
|
|
|
|
return done(err)
|
|
|
|
}
|
|
|
|
if ref, ok := desc.Annotations["containerd.io/distribution.source.ref"]; ok {
|
|
|
|
hf, err := docker.AppendDistributionSourceLabel(w.ContentStore(), ref)
|
|
|
|
if err != nil {
|
|
|
|
return done(err)
|
|
|
|
}
|
|
|
|
_, err = hf(ctx, desc)
|
|
|
|
return done(err)
|
|
|
|
}
|
|
|
|
return done(nil)
|
2018-04-13 21:13:48 +00:00
|
|
|
})
|
|
|
|
}(desc)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := eg.Wait(); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
unpackProgressDone := oneOffProgress(ctx, "unpacking")
|
2019-06-10 20:23:31 +00:00
|
|
|
defer func() {
|
2019-09-18 00:18:32 +00:00
|
|
|
err = unpackProgressDone(err)
|
2019-06-10 20:23:31 +00:00
|
|
|
}()
|
2019-09-18 00:18:32 +00:00
|
|
|
var current cache.ImmutableRef
|
|
|
|
for i, desc := range remote.Descriptors {
|
2018-05-05 05:18:11 +00:00
|
|
|
tm := time.Now()
|
2019-09-18 00:18:32 +00:00
|
|
|
if tmstr, ok := desc.Annotations[labelCreatedAt]; ok {
|
2018-05-05 05:18:11 +00:00
|
|
|
if err := (&tm).UnmarshalText([]byte(tmstr)); err != nil {
|
2019-09-19 18:58:59 +00:00
|
|
|
if current != nil {
|
|
|
|
current.Release(context.TODO())
|
|
|
|
}
|
2018-05-05 05:18:11 +00:00
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
2019-01-18 22:46:13 +00:00
|
|
|
descr := fmt.Sprintf("imported %s", remote.Descriptors[i].Digest)
|
2019-09-18 00:18:32 +00:00
|
|
|
if v, ok := desc.Annotations["buildkit/description"]; ok {
|
2019-01-18 22:46:13 +00:00
|
|
|
descr = v
|
|
|
|
}
|
2019-09-18 00:18:32 +00:00
|
|
|
ref, err := w.CacheManager.GetByBlob(ctx, desc, current, cache.WithDescription(descr), cache.WithCreationTime(tm))
|
|
|
|
if current != nil {
|
|
|
|
current.Release(context.TODO())
|
|
|
|
}
|
2018-05-05 05:18:11 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2019-09-18 00:18:32 +00:00
|
|
|
if err := ref.Extract(ctx); err != nil {
|
2019-09-19 18:58:59 +00:00
|
|
|
ref.Release(context.TODO())
|
2019-09-18 00:18:32 +00:00
|
|
|
return nil, err
|
2018-05-05 05:18:11 +00:00
|
|
|
}
|
2019-09-18 00:18:32 +00:00
|
|
|
current = ref
|
2018-05-05 05:18:11 +00:00
|
|
|
}
|
2019-09-18 00:18:32 +00:00
|
|
|
return current, nil
|
2018-04-13 21:13:48 +00:00
|
|
|
}
|
|
|
|
|
2018-06-24 05:59:17 +00:00
|
|
|
// Labels returns default labels
|
2017-12-19 09:34:34 +00:00
|
|
|
// utility function. could be moved to the constructor logic?
|
|
|
|
func Labels(executor, snapshotter string) map[string]string {
|
|
|
|
hostname, err := os.Hostname()
|
|
|
|
if err != nil {
|
|
|
|
hostname = "unknown"
|
|
|
|
}
|
|
|
|
labels := map[string]string{
|
|
|
|
worker.LabelExecutor: executor,
|
|
|
|
worker.LabelSnapshotter: snapshotter,
|
|
|
|
worker.LabelHostname: hostname,
|
|
|
|
}
|
|
|
|
return labels
|
|
|
|
}
|
|
|
|
|
|
|
|
// ID reads the worker id from the `workerid` file.
|
|
|
|
// If not exist, it creates a random one,
|
|
|
|
func ID(root string) (string, error) {
|
|
|
|
f := filepath.Join(root, "workerid")
|
|
|
|
b, err := ioutil.ReadFile(f)
|
|
|
|
if err != nil {
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
id := identity.NewID()
|
|
|
|
err := ioutil.WriteFile(f, []byte(id), 0400)
|
|
|
|
return id, err
|
|
|
|
} else {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return string(b), nil
|
|
|
|
}
|
2018-04-13 21:13:48 +00:00
|
|
|
|
|
|
|
func oneOffProgress(ctx context.Context, id string) func(err error) error {
|
|
|
|
pw, _, _ := progress.FromContext(ctx)
|
|
|
|
now := time.Now()
|
|
|
|
st := progress.Status{
|
|
|
|
Started: &now,
|
|
|
|
}
|
|
|
|
pw.Write(id, st)
|
|
|
|
return func(err error) error {
|
|
|
|
// TODO: set error on status
|
|
|
|
now := time.Now()
|
|
|
|
st.Completed = &now
|
|
|
|
pw.Write(id, st)
|
|
|
|
pw.Close()
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|