inlinecache.go 4.7 KB

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