github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/containerd/image_history.go (about)

     1  package containerd
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  
     7  	imagetype "github.com/Prakhar-Agarwal-byte/moby/api/types/image"
     8  	"github.com/Prakhar-Agarwal-byte/moby/errdefs"
     9  	"github.com/Prakhar-Agarwal-byte/moby/pkg/platforms"
    10  	"github.com/containerd/containerd/images"
    11  	cplatforms "github.com/containerd/containerd/platforms"
    12  	"github.com/containerd/log"
    13  	"github.com/distribution/reference"
    14  	"github.com/opencontainers/image-spec/identity"
    15  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  // ImageHistory returns a slice of HistoryResponseItem structures for the
    20  // specified image name by walking the image lineage.
    21  func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) {
    22  	img, err := i.resolveImage(ctx, name)
    23  	if err != nil {
    24  		return nil, err
    25  	}
    26  
    27  	cs := i.client.ContentStore()
    28  	// TODO: pass platform in from the CLI
    29  	platform := platforms.AllPlatformsWithPreference(cplatforms.Default())
    30  
    31  	var presentImages []ocispec.Image
    32  	err = i.walkImageManifests(ctx, img, func(img *ImageManifest) error {
    33  		conf, err := img.Config(ctx)
    34  		if err != nil {
    35  			return err
    36  		}
    37  		var ociimage ocispec.Image
    38  		if err := readConfig(ctx, cs, conf, &ociimage); err != nil {
    39  			return err
    40  		}
    41  		presentImages = append(presentImages, ociimage)
    42  		return nil
    43  	})
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	if len(presentImages) == 0 {
    48  		return nil, errdefs.NotFound(errors.New("failed to find image manifest"))
    49  	}
    50  
    51  	sort.SliceStable(presentImages, func(i, j int) bool {
    52  		return platform.Less(presentImages[i].Platform, presentImages[j].Platform)
    53  	})
    54  	ociimage := presentImages[0]
    55  
    56  	var (
    57  		history []*imagetype.HistoryResponseItem
    58  		sizes   []int64
    59  	)
    60  	s := i.client.SnapshotService(i.snapshotter)
    61  
    62  	diffIDs := ociimage.RootFS.DiffIDs
    63  	for i := range diffIDs {
    64  		chainID := identity.ChainID(diffIDs[0 : i+1]).String()
    65  
    66  		use, err := s.Usage(ctx, chainID)
    67  		if err != nil {
    68  			return nil, err
    69  		}
    70  
    71  		sizes = append(sizes, use.Size)
    72  	}
    73  
    74  	for _, h := range ociimage.History {
    75  		size := int64(0)
    76  		if !h.EmptyLayer {
    77  			if len(sizes) == 0 {
    78  				return nil, errors.New("unable to find the size of the layer")
    79  			}
    80  			size = sizes[0]
    81  			sizes = sizes[1:]
    82  		}
    83  
    84  		var created int64
    85  		if h.Created != nil {
    86  			created = h.Created.Unix()
    87  		}
    88  		history = append([]*imagetype.HistoryResponseItem{{
    89  			ID:        "<missing>",
    90  			Comment:   h.Comment,
    91  			CreatedBy: h.CreatedBy,
    92  			Created:   created,
    93  			Size:      size,
    94  			Tags:      nil,
    95  		}}, history...)
    96  	}
    97  
    98  	findParents := func(img images.Image) []images.Image {
    99  		imgs, err := i.getParentsByBuilderLabel(ctx, img)
   100  		if err != nil {
   101  			log.G(ctx).WithFields(log.Fields{
   102  				"error": err,
   103  				"image": img,
   104  			}).Warn("failed to list parent images")
   105  			return nil
   106  		}
   107  		return imgs
   108  	}
   109  
   110  	is := i.client.ImageService()
   111  	currentImg := img
   112  	for _, h := range history {
   113  		dgst := currentImg.Target.Digest.String()
   114  		h.ID = dgst
   115  
   116  		imgs, err := is.List(ctx, "target.digest=="+dgst)
   117  		if err != nil {
   118  			return nil, err
   119  		}
   120  
   121  		tags := getImageTags(ctx, imgs)
   122  		h.Tags = append(h.Tags, tags...)
   123  
   124  		parents := findParents(currentImg)
   125  
   126  		foundNext := false
   127  		for _, img := range parents {
   128  			_, hasLabel := img.Labels[imageLabelClassicBuilderParent]
   129  			if !foundNext || hasLabel {
   130  				currentImg = img
   131  				foundNext = true
   132  			}
   133  		}
   134  
   135  		if !foundNext {
   136  			break
   137  		}
   138  	}
   139  
   140  	return history, nil
   141  }
   142  
   143  func getImageTags(ctx context.Context, imgs []images.Image) []string {
   144  	var tags []string
   145  	for _, img := range imgs {
   146  		if isDanglingImage(img) {
   147  			continue
   148  		}
   149  
   150  		name, err := reference.ParseNamed(img.Name)
   151  		if err != nil {
   152  			log.G(ctx).WithFields(log.Fields{
   153  				"name":  name,
   154  				"error": err,
   155  			}).Warn("image with a name that's not a valid named reference")
   156  			continue
   157  		}
   158  
   159  		tags = append(tags, reference.FamiliarString(name))
   160  	}
   161  
   162  	return tags
   163  }