github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/containerd/image_children.go (about) 1 package containerd 2 3 import ( 4 "context" 5 6 "github.com/Prakhar-Agarwal-byte/moby/errdefs" 7 "github.com/Prakhar-Agarwal-byte/moby/image" 8 "github.com/containerd/containerd/content" 9 cerrdefs "github.com/containerd/containerd/errdefs" 10 containerdimages "github.com/containerd/containerd/images" 11 "github.com/containerd/containerd/platforms" 12 "github.com/containerd/log" 13 ocispec "github.com/opencontainers/image-spec/specs-go/v1" 14 "github.com/pkg/errors" 15 ) 16 17 // Children returns a slice of image IDs that are children of the `id` image 18 func (i *ImageService) Children(ctx context.Context, id image.ID) ([]image.ID, error) { 19 imgs, err := i.client.ImageService().List(ctx, "labels."+imageLabelClassicBuilderParent+"=="+string(id)) 20 if err != nil { 21 return []image.ID{}, errdefs.System(errors.Wrap(err, "failed to list all images")) 22 } 23 24 var children []image.ID 25 for _, img := range imgs { 26 children = append(children, image.ID(img.Target.Digest)) 27 } 28 29 return children, nil 30 } 31 32 // platformRootfs returns a rootfs for a specified platform. 33 func platformRootfs(ctx context.Context, store content.Store, desc ocispec.Descriptor, platform ocispec.Platform) (ocispec.RootFS, error) { 34 empty := ocispec.RootFS{} 35 36 configDesc, err := containerdimages.Config(ctx, store, desc, platforms.OnlyStrict(platform)) 37 if err != nil { 38 return empty, errors.Wrapf(err, "failed to get config for platform %s", platforms.Format(platform)) 39 } 40 41 diffs, err := containerdimages.RootFS(ctx, store, configDesc) 42 if err != nil { 43 return empty, errors.Wrapf(err, "failed to obtain rootfs") 44 } 45 46 return ocispec.RootFS{ 47 Type: "layers", 48 DiffIDs: diffs, 49 }, nil 50 } 51 52 // isRootfsChildOf checks if all layers from parent rootfs are child's first layers 53 // and child has at least one more layer (to make it not commutative). 54 // Example: 55 // A with layers [X, Y], 56 // B with layers [X, Y, Z] 57 // C with layers [Y, Z] 58 // 59 // Only isRootfsChildOf(B, A) is true. 60 // Which means that B is considered a children of A. B and C has no children. 61 // See more examples in TestIsRootfsChildOf. 62 func isRootfsChildOf(child ocispec.RootFS, parent ocispec.RootFS) bool { 63 childLen := len(child.DiffIDs) 64 parentLen := len(parent.DiffIDs) 65 66 if childLen <= parentLen { 67 return false 68 } 69 70 for i := 0; i < parentLen; i++ { 71 if child.DiffIDs[i] != parent.DiffIDs[i] { 72 return false 73 } 74 } 75 76 return true 77 } 78 79 // parents returns a slice of image IDs whose entire rootfs contents match, 80 // in order, the childs first layers, excluding images with the exact same 81 // rootfs. 82 // 83 // Called from image_delete.go to prune dangling parents. 84 func (i *ImageService) parents(ctx context.Context, id image.ID) ([]imageWithRootfs, error) { 85 target, err := i.resolveDescriptor(ctx, id.String()) 86 if err != nil { 87 return nil, errors.Wrap(err, "failed to get child image") 88 } 89 90 cs := i.client.ContentStore() 91 92 allPlatforms, err := containerdimages.Platforms(ctx, cs, target) 93 if err != nil { 94 return nil, errdefs.System(errors.Wrap(err, "failed to list platforms supported by image")) 95 } 96 97 var childRootFS []ocispec.RootFS 98 for _, platform := range allPlatforms { 99 rootfs, err := platformRootfs(ctx, cs, target, platform) 100 if err != nil { 101 if cerrdefs.IsNotFound(err) { 102 continue 103 } 104 return nil, errdefs.System(errors.Wrap(err, "failed to get platform-specific rootfs")) 105 } 106 107 childRootFS = append(childRootFS, rootfs) 108 } 109 110 imgs, err := i.client.ImageService().List(ctx) 111 if err != nil { 112 return nil, errdefs.System(errors.Wrap(err, "failed to list all images")) 113 } 114 115 var parents []imageWithRootfs 116 for _, img := range imgs { 117 nextImage: 118 for _, platform := range allPlatforms { 119 rootfs, err := platformRootfs(ctx, cs, img.Target, platform) 120 if err != nil { 121 if cerrdefs.IsNotFound(err) { 122 continue 123 } 124 return nil, errdefs.System(errors.Wrap(err, "failed to get platform-specific rootfs")) 125 } 126 127 for _, childRoot := range childRootFS { 128 if isRootfsChildOf(childRoot, rootfs) { 129 parents = append(parents, imageWithRootfs{ 130 img: img, 131 rootfs: rootfs, 132 }) 133 break nextImage 134 } 135 } 136 } 137 } 138 139 return parents, nil 140 } 141 142 // getParentsByBuilderLabel finds images that were a base for the given image 143 // by an image label set by the legacy builder. 144 // NOTE: This only works for images built with legacy builder (not Buildkit). 145 func (i *ImageService) getParentsByBuilderLabel(ctx context.Context, img containerdimages.Image) ([]containerdimages.Image, error) { 146 parent, ok := img.Labels[imageLabelClassicBuilderParent] 147 if !ok || parent == "" { 148 return nil, nil 149 } 150 151 dgst, err := digest.Parse(parent) 152 if err != nil { 153 log.G(ctx).WithFields(log.Fields{ 154 "error": err, 155 "value": parent, 156 }).Warnf("invalid %s label value", imageLabelClassicBuilderParent) 157 return nil, nil 158 } 159 160 return i.client.ImageService().List(ctx, "target.digest=="+dgst.String()) 161 } 162 163 type imageWithRootfs struct { 164 img containerdimages.Image 165 rootfs ocispec.RootFS 166 }