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
}

> the current implementation does indeed return `"Tags": null` but that just feels wrong for two reasons:
> 
> * there is no need for a slice to be nil, an empty slice avoids having to check  if it's null

While I agree with you in general, that returning an empty JSON array is more friendly to non-Go clients than `null`, it's rather futile to fix it here and now _because_ of the existing behaviour. Any client which wants to be compatible with any daemon older than v-next needs to be prepared to handle responses containing `"Tags": null`. And it's possible that some client could be using `history.Tags != null` to check if the image has any tags. I would love to make the API more consistent, but for the sake of forwards and backwards compatibility I think this change in behaviour needs to be API version-gated. (Furthermore, promoting the `nil` slice to an empty slice before marshaling to JSON depending on the API version is well within the scope of responsibility for an API handler function, IMO.)

In summary, my recommended course of action is to:
1. change this implementation to (implicitly) return `Tags: nil` to match the existing behaviour, and
2. follow-up with a PR which changes `(*imageRouter).getImagesHistory` to respond with `Tags: []` when API version >= 1.44