image_snapshot.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. package containerd
  2. import (
  3. "context"
  4. "fmt"
  5. "github.com/containerd/containerd"
  6. cerrdefs "github.com/containerd/containerd/errdefs"
  7. containerdimages "github.com/containerd/containerd/images"
  8. "github.com/containerd/containerd/leases"
  9. "github.com/containerd/containerd/platforms"
  10. "github.com/containerd/containerd/snapshots"
  11. "github.com/docker/docker/errdefs"
  12. "github.com/opencontainers/image-spec/identity"
  13. ocispec "github.com/opencontainers/image-spec/specs-go/v1"
  14. "github.com/pkg/errors"
  15. )
  16. // PrepareSnapshot prepares a snapshot from a parent image for a container
  17. func (i *ImageService) PrepareSnapshot(ctx context.Context, id string, parentImage string, platform *ocispec.Platform) error {
  18. img, err := i.resolveImage(ctx, parentImage)
  19. if err != nil {
  20. return err
  21. }
  22. cs := i.client.ContentStore()
  23. matcher := platforms.Default()
  24. if platform != nil {
  25. matcher = platforms.Only(*platform)
  26. }
  27. platformImg := containerd.NewImageWithPlatform(i.client, img, matcher)
  28. unpacked, err := platformImg.IsUnpacked(ctx, i.snapshotter)
  29. if err != nil {
  30. return err
  31. }
  32. if !unpacked {
  33. if err := platformImg.Unpack(ctx, i.snapshotter); err != nil {
  34. return err
  35. }
  36. }
  37. desc, err := containerdimages.Config(ctx, cs, img.Target, matcher)
  38. if err != nil {
  39. return err
  40. }
  41. diffIDs, err := containerdimages.RootFS(ctx, cs, desc)
  42. if err != nil {
  43. return err
  44. }
  45. parent := identity.ChainID(diffIDs).String()
  46. // Add a lease so that containerd doesn't garbage collect our snapshot
  47. ls := i.client.LeasesService()
  48. lease, err := ls.Create(ctx, leases.WithID(id))
  49. if err != nil {
  50. return err
  51. }
  52. if err := ls.AddResource(ctx, lease, leases.Resource{
  53. ID: id,
  54. Type: "snapshots/" + i.StorageDriver(),
  55. }); err != nil {
  56. return err
  57. }
  58. s := i.client.SnapshotService(i.StorageDriver())
  59. _, err = s.Prepare(ctx, id, parent)
  60. return err
  61. }
  62. // calculateSnapshotParentUsage returns the usage of all ancestors of the
  63. // provided snapshot. It doesn't include the size of the snapshot itself.
  64. func calculateSnapshotParentUsage(ctx context.Context, snapshotter snapshots.Snapshotter, snapshotID string) (snapshots.Usage, error) {
  65. info, err := snapshotter.Stat(ctx, snapshotID)
  66. if err != nil {
  67. if cerrdefs.IsNotFound(err) {
  68. return snapshots.Usage{}, errdefs.NotFound(err)
  69. }
  70. return snapshots.Usage{}, errdefs.System(errors.Wrapf(err, "snapshotter.Stat failed for %s", snapshotID))
  71. }
  72. if info.Parent == "" {
  73. return snapshots.Usage{}, errdefs.NotFound(fmt.Errorf("snapshot %s has no parent", snapshotID))
  74. }
  75. return calculateSnapshotTotalUsage(ctx, snapshotter, info.Parent)
  76. }
  77. // calculateSnapshotTotalUsage returns the total usage of that snapshot
  78. // including all of its ancestors.
  79. func calculateSnapshotTotalUsage(ctx context.Context, snapshotter snapshots.Snapshotter, snapshotID string) (snapshots.Usage, error) {
  80. var total snapshots.Usage
  81. next := snapshotID
  82. for next != "" {
  83. usage, err := snapshotter.Usage(ctx, next)
  84. if err != nil {
  85. if cerrdefs.IsNotFound(err) {
  86. return total, errdefs.NotFound(errors.Wrapf(err, "non-existing ancestor of %s", snapshotID))
  87. }
  88. return total, errdefs.System(errors.Wrapf(err, "snapshotter.Usage failed for %s", next))
  89. }
  90. total.Size += usage.Size
  91. total.Inodes += usage.Inodes
  92. info, err := snapshotter.Stat(ctx, next)
  93. if err != nil {
  94. return total, errdefs.System(errors.Wrapf(err, "snapshotter.Stat failed for %s", next))
  95. }
  96. next = info.Parent
  97. }
  98. return total, nil
  99. }