Files
moby/internal/testutils/specialimage/twoplatform.go
Paweł Gronowski 59169d0f97 image/inspect: Add platform selection
`GET /image/{name}/json` now supports `platform` parameter allowing to
specify which platform variant of a multi-platform image to inspect.

For servers that do not use containerd image store integration, this
option will cause an error if the requested platform doesn't match the
image's actual platform

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
2025-04-03 13:57:51 +02:00

114 lines
3.0 KiB
Go

package specialimage
import (
"os"
"path/filepath"
"github.com/containerd/platforms"
"github.com/distribution/reference"
"github.com/opencontainers/go-digest"
"github.com/opencontainers/image-spec/specs-go"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
func TwoPlatform(dir string) (*ocispec.Index, error) {
const imageRef = "twoplatform:latest"
ref, err := reference.ParseNormalizedNamed(imageRef)
if err != nil {
return nil, err
}
manifest1Desc, _, err := oneLayerPlatformManifest(dir, platforms.MustParse("linux/amd64"), FileInLayer{Path: "bash", Content: []byte("layer1")})
if err != nil {
return nil, err
}
manifest2Desc, _, err := oneLayerPlatformManifest(dir, platforms.MustParse("linux/arm64"), FileInLayer{Path: "bash", Content: []byte("layer2")})
if err != nil {
return nil, err
}
return multiPlatformImage(dir, ref, ocispec.Index{
Versioned: specs.Versioned{SchemaVersion: 2},
MediaType: ocispec.MediaTypeImageIndex,
Manifests: []ocispec.Descriptor{manifest1Desc, manifest2Desc},
})
}
type FileInLayer struct {
Path string
Content []byte
}
func oneLayerPlatformManifest(dir string, platform ocispec.Platform, f FileInLayer) (ocispec.Descriptor, manifestItem, error) {
layerDesc, err := writeLayerWithOneFile(dir, f.Path, f.Content)
if err != nil {
return ocispec.Descriptor{}, manifestItem{}, err
}
img := ocispec.Image{
Platform: platform,
Config: ocispec.ImageConfig{
Env: []string{"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"},
},
RootFS: ocispec.RootFS{
Type: "layers",
DiffIDs: []digest.Digest{layerDesc.Digest},
},
}
configDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageConfig, img)
if err != nil {
return ocispec.Descriptor{}, manifestItem{}, err
}
manifestDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageManifest, ocispec.Manifest{
MediaType: ocispec.MediaTypeImageManifest,
Config: configDesc,
Layers: []ocispec.Descriptor{layerDesc},
})
if err != nil {
return ocispec.Descriptor{}, manifestItem{}, err
}
manifestDesc.Platform = &platform
return manifestDesc, manifestItem{
Config: blobPath(configDesc),
Layers: []string{blobPath(layerDesc)},
}, nil
}
func multiPlatformImage(dir string, ref reference.Named, target ocispec.Index) (*ocispec.Index, error) {
targetDesc, err := writeJsonBlob(dir, ocispec.MediaTypeImageIndex, target)
if err != nil {
return nil, err
}
if ref != nil {
targetDesc.Annotations = map[string]string{
"io.containerd.image.name": ref.String(),
}
if tagged, ok := ref.(reference.Tagged); ok {
targetDesc.Annotations[ocispec.AnnotationRefName] = tagged.Tag()
}
}
index := ocispec.Index{
Versioned: specs.Versioned{SchemaVersion: 2},
MediaType: ocispec.MediaTypeImageIndex,
Manifests: []ocispec.Descriptor{targetDesc},
}
if err := writeJson(index, filepath.Join(dir, "index.json")); err != nil {
return nil, err
}
err = os.WriteFile(filepath.Join(dir, "oci-layout"), []byte(`{"imageLayoutVersion": "1.0.0"}`), 0o644)
if err != nil {
return nil, err
}
return &index, nil
}