github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/builder/builder-next/worker/worker.go (about)

     1  package worker
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  	nethttp "net/http"
     9  	"runtime"
    10  	"time"
    11  
    12  	"github.com/containerd/containerd/content"
    13  	"github.com/containerd/containerd/platforms"
    14  	"github.com/containerd/containerd/rootfs"
    15  	"github.com/docker/docker/distribution"
    16  	distmetadata "github.com/docker/docker/distribution/metadata"
    17  	"github.com/docker/docker/distribution/xfer"
    18  	"github.com/docker/docker/image"
    19  	"github.com/docker/docker/layer"
    20  	pkgprogress "github.com/docker/docker/pkg/progress"
    21  	"github.com/moby/buildkit/cache"
    22  	"github.com/moby/buildkit/cache/metadata"
    23  	"github.com/moby/buildkit/client"
    24  	"github.com/moby/buildkit/executor"
    25  	"github.com/moby/buildkit/exporter"
    26  	"github.com/moby/buildkit/frontend"
    27  	gw "github.com/moby/buildkit/frontend/gateway/client"
    28  	"github.com/moby/buildkit/session"
    29  	"github.com/moby/buildkit/snapshot"
    30  	"github.com/moby/buildkit/solver"
    31  	"github.com/moby/buildkit/solver/llbsolver/ops"
    32  	"github.com/moby/buildkit/solver/pb"
    33  	"github.com/moby/buildkit/source"
    34  	"github.com/moby/buildkit/source/git"
    35  	"github.com/moby/buildkit/source/http"
    36  	"github.com/moby/buildkit/source/local"
    37  	"github.com/moby/buildkit/util/contentutil"
    38  	"github.com/moby/buildkit/util/progress"
    39  	digest "github.com/opencontainers/go-digest"
    40  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    41  	"github.com/pkg/errors"
    42  	"github.com/sirupsen/logrus"
    43  )
    44  
    45  // Opt defines a structure for creating a worker.
    46  type Opt struct {
    47  	ID                string
    48  	Labels            map[string]string
    49  	GCPolicy          []client.PruneInfo
    50  	SessionManager    *session.Manager
    51  	MetadataStore     *metadata.Store
    52  	Executor          executor.Executor
    53  	Snapshotter       snapshot.Snapshotter
    54  	ContentStore      content.Store
    55  	CacheManager      cache.Manager
    56  	ImageSource       source.Source
    57  	Exporters         map[string]exporter.Exporter
    58  	DownloadManager   distribution.RootFSDownloadManager
    59  	V2MetadataService distmetadata.V2MetadataService
    60  	Transport         nethttp.RoundTripper
    61  }
    62  
    63  // Worker is a local worker instance with dedicated snapshotter, cache, and so on.
    64  // TODO: s/Worker/OpWorker/g ?
    65  type Worker struct {
    66  	Opt
    67  	SourceManager *source.Manager
    68  }
    69  
    70  // NewWorker instantiates a local worker
    71  func NewWorker(opt Opt) (*Worker, error) {
    72  	sm, err := source.NewManager()
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	cm := opt.CacheManager
    78  	sm.Register(opt.ImageSource)
    79  
    80  	gs, err := git.NewSource(git.Opt{
    81  		CacheAccessor: cm,
    82  		MetadataStore: opt.MetadataStore,
    83  	})
    84  	if err == nil {
    85  		sm.Register(gs)
    86  	} else {
    87  		logrus.Warnf("Could not register builder git source: %s", err)
    88  	}
    89  
    90  	hs, err := http.NewSource(http.Opt{
    91  		CacheAccessor: cm,
    92  		MetadataStore: opt.MetadataStore,
    93  		Transport:     opt.Transport,
    94  	})
    95  	if err == nil {
    96  		sm.Register(hs)
    97  	} else {
    98  		logrus.Warnf("Could not register builder http source: %s", err)
    99  	}
   100  
   101  	ss, err := local.NewSource(local.Opt{
   102  		SessionManager: opt.SessionManager,
   103  		CacheAccessor:  cm,
   104  		MetadataStore:  opt.MetadataStore,
   105  	})
   106  	if err == nil {
   107  		sm.Register(ss)
   108  	} else {
   109  		logrus.Warnf("Could not register builder local source: %s", err)
   110  	}
   111  
   112  	return &Worker{
   113  		Opt:           opt,
   114  		SourceManager: sm,
   115  	}, nil
   116  }
   117  
   118  // ID returns worker ID
   119  func (w *Worker) ID() string {
   120  	return w.Opt.ID
   121  }
   122  
   123  // Labels returns map of all worker labels
   124  func (w *Worker) Labels() map[string]string {
   125  	return w.Opt.Labels
   126  }
   127  
   128  // Platforms returns one or more platforms supported by the image.
   129  func (w *Worker) Platforms() []ocispec.Platform {
   130  	// does not handle lcow
   131  	return []ocispec.Platform{platforms.DefaultSpec()}
   132  }
   133  
   134  // GCPolicy returns automatic GC Policy
   135  func (w *Worker) GCPolicy() []client.PruneInfo {
   136  	return w.Opt.GCPolicy
   137  }
   138  
   139  // LoadRef loads a reference by ID
   140  func (w *Worker) LoadRef(id string, hidden bool) (cache.ImmutableRef, error) {
   141  	var opts []cache.RefOption
   142  	if hidden {
   143  		opts = append(opts, cache.NoUpdateLastUsed)
   144  	}
   145  	return w.CacheManager.Get(context.TODO(), id, opts...)
   146  }
   147  
   148  // ResolveOp converts a LLB vertex into a LLB operation
   149  func (w *Worker) ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge) (solver.Op, error) {
   150  	if baseOp, ok := v.Sys().(*pb.Op); ok {
   151  		switch op := baseOp.Op.(type) {
   152  		case *pb.Op_Source:
   153  			return ops.NewSourceOp(v, op, baseOp.Platform, w.SourceManager, w)
   154  		case *pb.Op_Exec:
   155  			return ops.NewExecOp(v, op, w.CacheManager, w.Opt.SessionManager, w.MetadataStore, w.Executor, w)
   156  		case *pb.Op_Build:
   157  			return ops.NewBuildOp(v, op, s, w)
   158  		}
   159  	}
   160  	return nil, errors.Errorf("could not resolve %v", v)
   161  }
   162  
   163  // ResolveImageConfig returns image config for an image
   164  func (w *Worker) ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error) {
   165  	// ImageSource is typically source/containerimage
   166  	resolveImageConfig, ok := w.ImageSource.(resolveImageConfig)
   167  	if !ok {
   168  		return "", nil, errors.Errorf("worker %q does not implement ResolveImageConfig", w.ID())
   169  	}
   170  	return resolveImageConfig.ResolveImageConfig(ctx, ref, opt)
   171  }
   172  
   173  // Exec executes a process directly on a worker
   174  func (w *Worker) Exec(ctx context.Context, meta executor.Meta, rootFS cache.ImmutableRef, stdin io.ReadCloser, stdout, stderr io.WriteCloser) error {
   175  	active, err := w.CacheManager.New(ctx, rootFS)
   176  	if err != nil {
   177  		return err
   178  	}
   179  	defer active.Release(context.TODO())
   180  	return w.Executor.Exec(ctx, meta, active, nil, stdin, stdout, stderr)
   181  }
   182  
   183  // DiskUsage returns disk usage report
   184  func (w *Worker) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) {
   185  	return w.CacheManager.DiskUsage(ctx, opt)
   186  }
   187  
   188  // Prune deletes reclaimable build cache
   189  func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, info ...client.PruneInfo) error {
   190  	return w.CacheManager.Prune(ctx, ch, info...)
   191  }
   192  
   193  // Exporter returns exporter by name
   194  func (w *Worker) Exporter(name string) (exporter.Exporter, error) {
   195  	exp, ok := w.Exporters[name]
   196  	if !ok {
   197  		return nil, errors.Errorf("exporter %q could not be found", name)
   198  	}
   199  	return exp, nil
   200  }
   201  
   202  // GetRemote returns a remote snapshot reference for a local one
   203  func (w *Worker) GetRemote(ctx context.Context, ref cache.ImmutableRef, createIfNeeded bool) (*solver.Remote, error) {
   204  	return nil, errors.Errorf("getremote not implemented")
   205  }
   206  
   207  // FromRemote converts a remote snapshot reference to a local one
   208  func (w *Worker) FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error) {
   209  	rootfs, err := getLayers(ctx, remote.Descriptors)
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	layers := make([]xfer.DownloadDescriptor, 0, len(rootfs))
   215  
   216  	for _, l := range rootfs {
   217  		// ongoing.add(desc)
   218  		layers = append(layers, &layerDescriptor{
   219  			desc:     l.Blob,
   220  			diffID:   layer.DiffID(l.Diff.Digest),
   221  			provider: remote.Provider,
   222  			w:        w,
   223  			pctx:     ctx,
   224  		})
   225  	}
   226  
   227  	defer func() {
   228  		for _, l := range rootfs {
   229  			w.ContentStore.Delete(context.TODO(), l.Blob.Digest)
   230  		}
   231  	}()
   232  
   233  	r := image.NewRootFS()
   234  	rootFS, release, err := w.DownloadManager.Download(ctx, *r, runtime.GOOS, layers, &discardProgress{})
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	defer release()
   239  
   240  	ref, err := w.CacheManager.GetFromSnapshotter(ctx, string(rootFS.ChainID()), cache.WithDescription(fmt.Sprintf("imported %s", remote.Descriptors[len(remote.Descriptors)-1].Digest)))
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  	return ref, nil
   245  }
   246  
   247  type discardProgress struct{}
   248  
   249  func (*discardProgress) WriteProgress(_ pkgprogress.Progress) error {
   250  	return nil
   251  }
   252  
   253  // Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error)
   254  type layerDescriptor struct {
   255  	provider content.Provider
   256  	desc     ocispec.Descriptor
   257  	diffID   layer.DiffID
   258  	// ref      ctdreference.Spec
   259  	w    *Worker
   260  	pctx context.Context
   261  }
   262  
   263  func (ld *layerDescriptor) Key() string {
   264  	return "v2:" + ld.desc.Digest.String()
   265  }
   266  
   267  func (ld *layerDescriptor) ID() string {
   268  	return ld.desc.Digest.String()
   269  }
   270  
   271  func (ld *layerDescriptor) DiffID() (layer.DiffID, error) {
   272  	return ld.diffID, nil
   273  }
   274  
   275  func (ld *layerDescriptor) Download(ctx context.Context, progressOutput pkgprogress.Output) (io.ReadCloser, int64, error) {
   276  	done := oneOffProgress(ld.pctx, fmt.Sprintf("pulling %s", ld.desc.Digest))
   277  	if err := contentutil.Copy(ctx, ld.w.ContentStore, ld.provider, ld.desc); err != nil {
   278  		return nil, 0, done(err)
   279  	}
   280  	done(nil)
   281  
   282  	ra, err := ld.w.ContentStore.ReaderAt(ctx, ld.desc)
   283  	if err != nil {
   284  		return nil, 0, err
   285  	}
   286  
   287  	return ioutil.NopCloser(content.NewReader(ra)), ld.desc.Size, nil
   288  }
   289  
   290  func (ld *layerDescriptor) Close() {
   291  	// ld.is.ContentStore.Delete(context.TODO(), ld.desc.Digest)
   292  }
   293  
   294  func (ld *layerDescriptor) Registered(diffID layer.DiffID) {
   295  	// Cache mapping from this layer's DiffID to the blobsum
   296  	ld.w.V2MetadataService.Add(diffID, distmetadata.V2Metadata{Digest: ld.desc.Digest})
   297  }
   298  
   299  func getLayers(ctx context.Context, descs []ocispec.Descriptor) ([]rootfs.Layer, error) {
   300  	layers := make([]rootfs.Layer, len(descs))
   301  	for i, desc := range descs {
   302  		diffIDStr := desc.Annotations["containerd.io/uncompressed"]
   303  		if diffIDStr == "" {
   304  			return nil, errors.Errorf("%s missing uncompressed digest", desc.Digest)
   305  		}
   306  		diffID, err := digest.Parse(diffIDStr)
   307  		if err != nil {
   308  			return nil, err
   309  		}
   310  		layers[i].Diff = ocispec.Descriptor{
   311  			MediaType: ocispec.MediaTypeImageLayer,
   312  			Digest:    diffID,
   313  		}
   314  		layers[i].Blob = ocispec.Descriptor{
   315  			MediaType: desc.MediaType,
   316  			Digest:    desc.Digest,
   317  			Size:      desc.Size,
   318  		}
   319  	}
   320  	return layers, nil
   321  }
   322  
   323  func oneOffProgress(ctx context.Context, id string) func(err error) error {
   324  	pw, _, _ := progress.FromContext(ctx)
   325  	now := time.Now()
   326  	st := progress.Status{
   327  		Started: &now,
   328  	}
   329  	pw.Write(id, st)
   330  	return func(err error) error {
   331  		// TODO: set error on status
   332  		now := time.Now()
   333  		st.Completed = &now
   334  		pw.Write(id, st)
   335  		pw.Close()
   336  		return err
   337  	}
   338  }
   339  
   340  type resolveImageConfig interface {
   341  	ResolveImageConfig(ctx context.Context, ref string, opt gw.ResolveImageConfigOpt) (digest.Digest, []byte, error)
   342  }