image_history.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package containerd
  2. import (
  3. "context"
  4. "sort"
  5. cplatforms "github.com/containerd/containerd/platforms"
  6. "github.com/docker/distribution/reference"
  7. imagetype "github.com/docker/docker/api/types/image"
  8. "github.com/docker/docker/errdefs"
  9. "github.com/docker/docker/pkg/platforms"
  10. "github.com/opencontainers/image-spec/identity"
  11. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  12. "github.com/pkg/errors"
  13. )
  14. // ImageHistory returns a slice of HistoryResponseItem structures for the
  15. // specified image name by walking the image lineage.
  16. func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) {
  17. desc, err := i.resolveImage(ctx, name)
  18. if err != nil {
  19. return nil, err
  20. }
  21. cs := i.client.ContentStore()
  22. // TODO: pass platform in from the CLI
  23. platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
  24. var presentImages []ocispec.Image
  25. err = i.walkImageManifests(ctx, desc, func(img *ImageManifest) error {
  26. conf, err := img.Config(ctx)
  27. if err != nil {
  28. return err
  29. }
  30. var ociimage ocispec.Image
  31. if err := readConfig(ctx, cs, conf, &ociimage); err != nil {
  32. return err
  33. }
  34. presentImages = append(presentImages, ociimage)
  35. return nil
  36. })
  37. if err != nil {
  38. return nil, err
  39. }
  40. if len(presentImages) == 0 {
  41. return nil, errdefs.NotFound(errors.New("failed to find image manifest"))
  42. }
  43. sort.SliceStable(presentImages, func(i, j int) bool {
  44. return platform.Less(presentImages[i].Platform, presentImages[j].Platform)
  45. })
  46. ociimage := presentImages[0]
  47. var (
  48. history []*imagetype.HistoryResponseItem
  49. sizes []int64
  50. )
  51. s := i.client.SnapshotService(i.snapshotter)
  52. diffIDs := ociimage.RootFS.DiffIDs
  53. for i := range diffIDs {
  54. chainID := identity.ChainID(diffIDs[0 : i+1]).String()
  55. use, err := s.Usage(ctx, chainID)
  56. if err != nil {
  57. return nil, err
  58. }
  59. sizes = append(sizes, use.Size)
  60. }
  61. for _, h := range ociimage.History {
  62. size := int64(0)
  63. if !h.EmptyLayer {
  64. if len(sizes) == 0 {
  65. return nil, errors.New("unable to find the size of the layer")
  66. }
  67. size = sizes[0]
  68. sizes = sizes[1:]
  69. }
  70. history = append([]*imagetype.HistoryResponseItem{{
  71. ID: "<missing>",
  72. Comment: h.Comment,
  73. CreatedBy: h.CreatedBy,
  74. Created: h.Created.Unix(),
  75. Size: size,
  76. Tags: nil,
  77. }}, history...)
  78. }
  79. if len(history) != 0 {
  80. history[0].ID = desc.Target.Digest.String()
  81. tagged, err := i.client.ImageService().List(ctx, "target.digest=="+desc.Target.Digest.String())
  82. if err != nil {
  83. return nil, err
  84. }
  85. var tags []string
  86. for _, t := range tagged {
  87. if isDanglingImage(t) {
  88. continue
  89. }
  90. name, err := reference.ParseNamed(t.Name)
  91. if err != nil {
  92. return nil, err
  93. }
  94. tags = append(tags, reference.FamiliarString(name))
  95. }
  96. history[0].Tags = tags
  97. }
  98. return history, nil
  99. }