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