daemon/c8d: Implement image history
**- What I did**

Implemented `docker image history`.

Note there is some duplication of code with the usual resolveDescriptor -> images.Config -> content.ReadBlob, I think we can refactor at a later time.

I'm unsure how `Tags` work, there is a difference between an image history when it's built by the classic builder or buildkit:

Currently, with this Dockerfile:
```dockerfile
FROM alpine

CMD ["echo", "hello"]
```

If we build it with the classic builder (``) we get this history:

```json
[
  {
    "Comment": "",
    "Created": 1673531696,
    "CreatedBy": "/bin/sh -c #(nop)  CMD [\"echo\" \"hello\"]",
    "Id": "sha256:42f9b7e41f28aa91037a8c8355de66d650c6fd192426e7d30c33591a5e72e6ea",
    "Size": 0,
    "Tags": [
      "henelo:latest"
    ]
  },
  {
    "Comment": "",
    "Created": 1673283889,
    "CreatedBy": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
    "Id": "sha256:04eeaa5f8c35b8206a15c12425498c37ecd2181e0ef202bea24a2382e4dd240f",
    "Size": 0,
    "Tags": [
      "alpine:latest"
    ]
  },
  {
    "Comment": "",
    "Created": 1673283888,
    "CreatedBy": "/bin/sh -c #(nop) ADD file:3080f19f39259a4b77cc53975de0184c78d4335ceb9ffb77a2838d0539ad6f85 in / ",
    "Id": "<missing>",
    "Size": 7458929,
    "Tags": null
  }
]
```

But if we build it with buidkit we get this:

```json
[
  {
    "Comment": "buildkit.dockerfile.v0",
    "Created": 1673283889,
    "CreatedBy": "CMD [\"echo\" \"hello\"]",
    "Id": "sha256:6f6cafc4e6423f0997d34c88b4e018028c5225c6ccb67f55c2f0efd5f8a808aa",
    "Size": 0,
    "Tags": [
      "henelo:latest"
    ]
  },
  {
    "Comment": "",
    "Created": 1673283889,
    "CreatedBy": "/bin/sh -c #(nop)  CMD [\"/bin/sh\"]",
    "Id": "<missing>",
    "Size": 0,
    "Tags": null
  },
  {
    "Comment": "",
    "Created": 1673283888,
    "CreatedBy": "/bin/sh -c #(nop) ADD file:3080f19f39259a4b77cc53975de0184c78d4335ceb9ffb77a2838d0539ad6f85 in / ",
    "Id": "<missing>",
    "Size": 7458929,
    "Tags": null
  }
]
```

Note how the image that's built with buildkit looses the `alpine:latest` tags in the second history item.

Also, the only way to see these `Tags` is to call the API directly, `docker image history <name> --format '{{ json . }}'` doesn't even show the `Tags` property.

~~Another difference with the exising history API is that the `Tags` will not be `null` since the docs say that `Tags` is required.~~

Upstreams:
* https://github.com/rumpl/moby/pull/69

**- How I did it**

By looping over the `History` of the image.

**- How to verify it**

Pull or build an image and then call `docker image history`.

**- Description for the changelog**

**- A picture of a cute animal (not mandatory but encouraged)**
<img width="750" alt="Screenshot 2023-01-12 at 14 40 37" src="https://user-images.githubusercontent.com/99933/212081796-e8a5908a-02ee-4f19-a7c8-d9396648a689.png">

daemon/containerd/image_history.go
package containerd

import (
	"context"
	"errors"

	imagetype "github.com/docker/docker/api/types/image"
	"github.com/docker/docker/errdefs"
)

// ImageHistory returns a slice of ImageHistory structures for the specified
// image name by walking the image lineage.
func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) {
	return nil, errdefs.NotImplemented(errors.New("not implemented"))
}

package containerd

import (
	"context"
	"encoding/json"

	"github.com/containerd/containerd/content"
	containerdimages "github.com/containerd/containerd/images"
	cplatforms "github.com/containerd/containerd/platforms"
	"github.com/docker/distribution/reference"
	imagetype "github.com/docker/docker/api/types/image"
	"github.com/docker/docker/pkg/platforms"
	"github.com/opencontainers/image-spec/identity"
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
	"github.com/pkg/errors"
)

// ImageHistory returns a slice of HistoryResponseItem structures for the
// specified image name by walking the image lineage.
func (i *ImageService) ImageHistory(ctx context.Context, name string) ([]*imagetype.HistoryResponseItem, error) {
	desc, err := i.resolveDescriptor(ctx, name)
	if err != nil {
		return nil, err
	}

	cs := i.client.ContentStore()
	// TODO: pass the platform from the cli
	conf, err := containerdimages.Config(ctx, cs, desc, platforms.AllPlatformsWithPreference(cplatforms.Default()))
	if err != nil {
		return nil, err
	}

	diffIDs, err := containerdimages.RootFS(ctx, cs, conf)
	if err != nil {
		return nil, err
	}

	blob, err := content.ReadBlob(ctx, cs, conf)
	if err != nil {
		return nil, err
	}

	var image ocispec.Image
	if err := json.Unmarshal(blob, &image); err != nil {
		return nil, err
	}

	var (
		history []*imagetype.HistoryResponseItem
		sizes   []int64
	)
	s := i.client.SnapshotService(i.snapshotter)

	for i := range diffIDs {
		chainID := identity.ChainID(diffIDs[0 : i+1]).String()

		use, err := s.Usage(ctx, chainID)
		if err != nil {
			return nil, err
		}

		sizes = append(sizes, use.Size)
	}

	for _, h := range image.History {
		size := int64(0)
		if !h.EmptyLayer {
			if len(sizes) == 0 {
				return nil, errors.New("unable to find the size of the layer")
			}
			size = sizes[0]
			sizes = sizes[1:]
		}

		history = append([]*imagetype.HistoryResponseItem{{
			ID:        "<missing>",
			Comment:   h.Comment,
			CreatedBy: h.CreatedBy,
			Created:   h.Created.Unix(),
			Size:      size,
			Tags:      nil,
		}}, history...)
	}

	if len(history) != 0 {
		history[0].ID = desc.Digest.String()

		tagged, err := i.client.ImageService().List(ctx, "target.digest=="+desc.Digest.String())
		if err != nil {
			return nil, err
		}

		tags := make([]string, len(tagged))
		for i, t := range tagged {
			name, err := reference.ParseNamed(t.Name)
			if err != nil {
				return nil, err
			}
			tags[i] = reference.FamiliarString(name)
		}
		history[0].Tags = tags
	}

	return history, nil
}

As a future enhancement, wouldn't it be better for this to use the content store's `ReaderAt` method and pass that directly to an `encoding/json`'s streaming interface so we don't have to have the full blob in memory _and_ the full parsed object? :sweat_smile: