mirror of
https://github.com/crazy-max/diun.git
synced 2025-01-12 11:38:11 +00:00
116 lines
3.6 KiB
Go
116 lines
3.6 KiB
Go
package registry
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/containers/image/v5/docker"
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/opencontainers/go-digest"
|
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Manifest is the Docker image manifest information
|
|
type Manifest struct {
|
|
Name string
|
|
Tag string
|
|
MIMEType string
|
|
Digest digest.Digest
|
|
Created *time.Time
|
|
DockerVersion string
|
|
Labels map[string]string
|
|
Layers []string
|
|
Platform string
|
|
Raw []byte
|
|
}
|
|
|
|
// Manifest returns the manifest for a specific image
|
|
func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, error) {
|
|
ctx, cancel := c.timeoutContext()
|
|
defer cancel()
|
|
|
|
rmRef, err := ImageReference(image.String())
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot parse reference")
|
|
}
|
|
|
|
// Retrieve remote digest through HEAD request
|
|
rmDigest, err := docker.GetDigest(ctx, c.sysCtx, rmRef)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot get image digest from HEAD request")
|
|
}
|
|
|
|
// Digest match, returns db manifest
|
|
if c.opts.CompareDigest && len(dbManifest.Digest) > 0 && dbManifest.Digest == rmDigest {
|
|
return dbManifest, false, nil
|
|
}
|
|
|
|
rmCloser, err := rmRef.NewImage(ctx, c.sysCtx)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot create image closer")
|
|
}
|
|
defer rmCloser.Close()
|
|
|
|
rmRawManifest, rmManifestMimeType, err := rmCloser.Manifest(ctx)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot get raw manifest")
|
|
}
|
|
|
|
// For manifests list compare also digest matching the platform
|
|
updated := dbManifest.Digest != rmDigest
|
|
if c.opts.CompareDigest && len(dbManifest.Raw) > 0 && dbManifest.isManifestList() && isManifestList(rmManifestMimeType) {
|
|
dbManifestList, err := manifest.ListFromBlob(dbManifest.Raw, dbManifest.MIMEType)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot parse manifest list")
|
|
}
|
|
dbManifestPlatformDigest, err := dbManifestList.ChooseInstance(c.sysCtx)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrapf(err, "error choosing image instance")
|
|
}
|
|
rmManifestList, err := manifest.ListFromBlob(rmRawManifest, rmManifestMimeType)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot parse manifest list")
|
|
}
|
|
rmManifestPlatformDigest, err := rmManifestList.ChooseInstance(c.sysCtx)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrapf(err, "error choosing image instance")
|
|
}
|
|
updated = dbManifestPlatformDigest != rmManifestPlatformDigest
|
|
}
|
|
|
|
// Metadata describing the Docker image
|
|
rmInspect, err := rmCloser.Inspect(ctx)
|
|
if err != nil {
|
|
return Manifest{}, false, errors.Wrap(err, "cannot inspect")
|
|
}
|
|
rmTag := rmInspect.Tag
|
|
if len(rmTag) == 0 {
|
|
rmTag = image.Tag
|
|
}
|
|
rmPlatform := fmt.Sprintf("%s/%s", rmInspect.Os, rmInspect.Architecture)
|
|
if rmInspect.Variant != "" {
|
|
rmPlatform = fmt.Sprintf("%s/%s", rmPlatform, rmInspect.Variant)
|
|
}
|
|
|
|
return Manifest{
|
|
Name: rmCloser.Reference().DockerReference().Name(),
|
|
Tag: rmTag,
|
|
MIMEType: rmManifestMimeType,
|
|
Digest: rmDigest,
|
|
Created: rmInspect.Created,
|
|
DockerVersion: rmInspect.DockerVersion,
|
|
Labels: rmInspect.Labels,
|
|
Layers: rmInspect.Layers,
|
|
Platform: rmPlatform,
|
|
Raw: rmRawManifest,
|
|
}, updated, nil
|
|
}
|
|
|
|
func (m Manifest) isManifestList() bool {
|
|
return isManifestList(m.MIMEType)
|
|
}
|
|
|
|
func isManifestList(mimeType string) bool {
|
|
return mimeType == manifest.DockerV2ListMediaType || mimeType == imgspecv1.MediaTypeImageIndex
|
|
}
|