func/pkg/docker/platform.go

76 lines
2.2 KiB
Go

package docker
import (
"fmt"
"github.com/containerd/platforms"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote"
gcrTypes "github.com/google/go-containerregistry/pkg/v1/types"
)
// GetPlatformImage returns image reference for specific platform.
// If the image is not multi-arch it returns ref argument directly (provided platform matches).
// If the image is multi-arch it returns digest based reference (provided the platform is part of the multi-arch image).
func GetPlatformImage(ref, platform string) (string, error) {
plat, err := platforms.Parse(platform)
if err != nil {
return "", fmt.Errorf("cannot parse platform: %w", err)
}
r, err := name.ParseReference(ref)
if err != nil {
return "", fmt.Errorf("cannot parse reference: %w", err)
}
desc, err := remote.Get(r)
if err != nil {
return "", fmt.Errorf("cannot get remote image: %w", err)
}
if desc.MediaType != gcrTypes.OCIImageIndex && desc.MediaType != gcrTypes.DockerManifestList {
// it's non-multi-arch image
var img v1.Image
var cfg *v1.ConfigFile
img, err = desc.Image()
if err != nil {
return "", fmt.Errorf("cannot get image from the descriptor: %w", err)
}
cfg, err = img.ConfigFile()
if err != nil {
return "", fmt.Errorf("cannot get config file for the image: %w", err)
}
if plat.OS == cfg.OS &&
plat.Architecture == cfg.Architecture {
return ref, nil
}
return "", fmt.Errorf("the %q platform is not supported by the %q image", platform, ref)
}
idx, err := desc.ImageIndex()
if err != nil {
return "", fmt.Errorf("cannot get image index: %w", err)
}
idxMft, err := idx.IndexManifest()
if err != nil {
return "", fmt.Errorf("cannot get index manifest: %w", err)
}
if len(idxMft.Manifests) > 1000 {
return "", fmt.Errorf("platform image has too many manifests")
}
for _, manifest := range idxMft.Manifests {
if plat.OS == manifest.Platform.OS &&
plat.Architecture == manifest.Platform.Architecture {
return r.Context().Name() + "@" + manifest.Digest.String(), nil
}
}
return "", fmt.Errorf("the %q platform is not supported by the %q image", platform, ref)
}