gopkg.in/docker/docker.v20@v20.10.27/builder/builder-next/adapters/localinlinecache/inlinecache.go (about)

     1  package localinlinecache
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"time"
     7  
     8  	"github.com/containerd/containerd/content"
     9  	"github.com/containerd/containerd/images"
    10  	"github.com/containerd/containerd/remotes/docker"
    11  	distreference "github.com/docker/distribution/reference"
    12  	imagestore "github.com/docker/docker/image"
    13  	"github.com/docker/docker/reference"
    14  	"github.com/moby/buildkit/cache/remotecache"
    15  	registryremotecache "github.com/moby/buildkit/cache/remotecache/registry"
    16  	v1 "github.com/moby/buildkit/cache/remotecache/v1"
    17  	"github.com/moby/buildkit/session"
    18  	"github.com/moby/buildkit/solver"
    19  	"github.com/moby/buildkit/worker"
    20  	digest "github.com/opencontainers/go-digest"
    21  	specs "github.com/opencontainers/image-spec/specs-go/v1"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  func init() {
    26  	// See https://github.com/moby/buildkit/pull/1993.
    27  	v1.EmptyLayerRemovalSupported = false
    28  }
    29  
    30  // ResolveCacheImporterFunc returns a resolver function for local inline cache
    31  func ResolveCacheImporterFunc(sm *session.Manager, resolverFunc docker.RegistryHosts, cs content.Store, rs reference.Store, is imagestore.Store) remotecache.ResolveCacheImporterFunc {
    32  
    33  	upstream := registryremotecache.ResolveCacheImporterFunc(sm, cs, resolverFunc)
    34  
    35  	return func(ctx context.Context, group session.Group, attrs map[string]string) (remotecache.Importer, specs.Descriptor, error) {
    36  		if dt, err := tryImportLocal(rs, is, attrs["ref"]); err == nil {
    37  			return newLocalImporter(dt), specs.Descriptor{}, nil
    38  		}
    39  		return upstream(ctx, group, attrs)
    40  	}
    41  }
    42  
    43  func tryImportLocal(rs reference.Store, is imagestore.Store, refStr string) ([]byte, error) {
    44  	ref, err := distreference.ParseNormalizedNamed(refStr)
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  	dgst, err := rs.Get(ref)
    49  	if err != nil {
    50  		return nil, err
    51  	}
    52  	img, err := is.Get(imagestore.ID(dgst))
    53  	if err != nil {
    54  		return nil, err
    55  	}
    56  
    57  	return img.RawJSON(), nil
    58  }
    59  
    60  func newLocalImporter(dt []byte) remotecache.Importer {
    61  	return &localImporter{dt: dt}
    62  }
    63  
    64  type localImporter struct {
    65  	dt []byte
    66  }
    67  
    68  func (li *localImporter) Resolve(ctx context.Context, _ specs.Descriptor, id string, w worker.Worker) (solver.CacheManager, error) {
    69  	cc := v1.NewCacheChains()
    70  	if err := li.importInlineCache(ctx, li.dt, cc); err != nil {
    71  		return nil, err
    72  	}
    73  
    74  	keysStorage, resultStorage, err := v1.NewCacheKeyStorage(cc, w)
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	return solver.NewCacheManager(id, keysStorage, resultStorage), nil
    79  }
    80  
    81  func (li *localImporter) importInlineCache(ctx context.Context, dt []byte, cc solver.CacheExporterTarget) error {
    82  	var img image
    83  
    84  	if err := json.Unmarshal(dt, &img); err != nil {
    85  		return err
    86  	}
    87  
    88  	if img.Cache == nil {
    89  		return nil
    90  	}
    91  
    92  	var config v1.CacheConfig
    93  	if err := json.Unmarshal(img.Cache, &config.Records); err != nil {
    94  		return err
    95  	}
    96  
    97  	createdDates, createdMsg, err := parseCreatedLayerInfo(img)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	layers := v1.DescriptorProvider{}
   103  	for i, diffID := range img.Rootfs.DiffIDs {
   104  		dgst := digest.Digest(diffID.String())
   105  		desc := specs.Descriptor{
   106  			Digest:      dgst,
   107  			Size:        -1,
   108  			MediaType:   images.MediaTypeDockerSchema2Layer,
   109  			Annotations: map[string]string{},
   110  		}
   111  		if createdAt := createdDates[i]; createdAt != "" {
   112  			desc.Annotations["buildkit/createdat"] = createdAt
   113  		}
   114  		if createdBy := createdMsg[i]; createdBy != "" {
   115  			desc.Annotations["buildkit/description"] = createdBy
   116  		}
   117  		desc.Annotations["containerd.io/uncompressed"] = img.Rootfs.DiffIDs[i].String()
   118  		layers[dgst] = v1.DescriptorProviderPair{
   119  			Descriptor: desc,
   120  			Provider:   &emptyProvider{},
   121  		}
   122  		config.Layers = append(config.Layers, v1.CacheLayer{
   123  			Blob:        dgst,
   124  			ParentIndex: i - 1,
   125  		})
   126  	}
   127  
   128  	return v1.ParseConfig(config, layers, cc)
   129  }
   130  
   131  type image struct {
   132  	Rootfs struct {
   133  		DiffIDs []digest.Digest `json:"diff_ids"`
   134  	} `json:"rootfs"`
   135  	Cache   []byte `json:"moby.buildkit.cache.v0"`
   136  	History []struct {
   137  		Created    *time.Time `json:"created,omitempty"`
   138  		CreatedBy  string     `json:"created_by,omitempty"`
   139  		EmptyLayer bool       `json:"empty_layer,omitempty"`
   140  	} `json:"history,omitempty"`
   141  }
   142  
   143  func parseCreatedLayerInfo(img image) ([]string, []string, error) {
   144  	dates := make([]string, 0, len(img.Rootfs.DiffIDs))
   145  	createdBy := make([]string, 0, len(img.Rootfs.DiffIDs))
   146  	for _, h := range img.History {
   147  		if !h.EmptyLayer {
   148  			str := ""
   149  			if h.Created != nil {
   150  				dt, err := h.Created.MarshalText()
   151  				if err != nil {
   152  					return nil, nil, err
   153  				}
   154  				str = string(dt)
   155  			}
   156  			dates = append(dates, str)
   157  			createdBy = append(createdBy, h.CreatedBy)
   158  		}
   159  	}
   160  	return dates, createdBy, nil
   161  }
   162  
   163  type emptyProvider struct {
   164  }
   165  
   166  func (p *emptyProvider) ReaderAt(ctx context.Context, dec specs.Descriptor) (content.ReaderAt, error) {
   167  	return nil, errors.Errorf("ReaderAt not implemented for empty provider")
   168  }