mirror of
https://github.com/containers/podman.git
synced 2025-07-23 23:20:48 +08:00

this pr represents a shift in how we download podman machine images. these images will now be stored in oci registry and will replace the default method of downloading an image. you can still use a reference to a disk image as a path or url too with the --image-path switch. the final registry and location of the images has not been determined; and will need to be updated in podman as well. i dont think we need to allow --image-path to accept a registry/image for the podman 5.0 release. i do think there will be demand for this. upgrades also need to be plumbed. for example, updating from an oci registry. once we make decisions on final image locations/registrties as well as some behaviors of init and the oci pull, we must update the machine-init documentation. Signed-off-by: Brent Baude <bbaude@redhat.com>
160 lines
4.4 KiB
Go
160 lines
4.4 KiB
Go
package ocipull
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/containers/image/v5/docker"
|
|
"github.com/containers/image/v5/manifest"
|
|
"github.com/containers/image/v5/oci/layout"
|
|
"github.com/containers/image/v5/types"
|
|
"github.com/opencontainers/go-digest"
|
|
specV1 "github.com/opencontainers/image-spec/specs-go/v1"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// readManifestFromImageSource reads the manifest from the specified image
|
|
// source. Note that the manifest is expected to be an OCI v1 manifest.
|
|
func readManifestFromImageSource(ctx context.Context, src types.ImageSource) (*specV1.Manifest, *digest.Digest, int64, error) {
|
|
rawData, mimeType, err := src.GetManifest(ctx, nil)
|
|
if err != nil {
|
|
return nil, nil, -1, err
|
|
}
|
|
if mimeType != specV1.MediaTypeImageManifest {
|
|
return nil, nil, -1, fmt.Errorf("image %q is of type %q (expected: %q)", strings.TrimPrefix(src.Reference().StringWithinTransport(), "//"), mimeType, specV1.MediaTypeImageManifest)
|
|
}
|
|
|
|
mannyFest := specV1.Manifest{}
|
|
if err := json.Unmarshal(rawData, &mannyFest); err != nil {
|
|
return nil, nil, -1, fmt.Errorf("reading manifest: %w", err)
|
|
}
|
|
|
|
manifestDigest := digest.FromBytes(rawData)
|
|
return &mannyFest, &manifestDigest, int64(len(rawData)), nil
|
|
}
|
|
|
|
func GetLocalBlob(ctx context.Context, path string) (*types.BlobInfo, error) {
|
|
ociRef, err := layout.ParseReference(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
img, err := ociRef.NewImage(ctx, &types.SystemContext{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, _, err := img.Manifest(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
localManifest := specV1.Manifest{}
|
|
if err := json.Unmarshal(b, &localManifest); err != nil {
|
|
return nil, err
|
|
}
|
|
blobs := img.LayerInfos()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(blobs) != 1 {
|
|
return nil, errors.New("invalid disk image")
|
|
}
|
|
fmt.Println(blobs[0].Digest.Hex())
|
|
return &blobs[0], nil
|
|
}
|
|
|
|
func GetRemoteManifest(ctx context.Context, dest string) (*specV1.Manifest, error) { //nolint:unused
|
|
ref, err := docker.ParseReference(fmt.Sprintf("//%s", dest))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
imgSrc, err := ref.NewImage(ctx, &types.SystemContext{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
b, _, err := imgSrc.Manifest(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
remoteManifest := specV1.Manifest{}
|
|
err = json.Unmarshal(b, &remoteManifest)
|
|
return &remoteManifest, err
|
|
}
|
|
|
|
func GetDiskArtifactReference(ctx context.Context, imgSrc types.ImageSource, opts *DiskArtifactOpts) (digest.Digest, error) {
|
|
rawMannyFest, mannyType, err := imgSrc.GetManifest(ctx, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
if !manifest.MIMETypeIsMultiImage(mannyType) { // if not true
|
|
return "", fmt.Errorf("wrong manifest type for disk artifact: %s", mannyType)
|
|
}
|
|
|
|
mannyFestList, err := manifest.ListFromBlob(rawMannyFest, mannyType)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to parse manifest list from blob: %q", err)
|
|
}
|
|
|
|
var (
|
|
artifactDigest digest.Digest
|
|
)
|
|
for _, d := range mannyFestList.Instances() {
|
|
bar, err := mannyFestList.Instance(d)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
val, ok := bar.ReadOnly.Annotations["disktype"]
|
|
if !ok { // quick exit, no type match
|
|
continue
|
|
}
|
|
// wrong arch
|
|
if bar.ReadOnly.Platform.Architecture != opts.arch {
|
|
continue
|
|
}
|
|
// wrong os
|
|
if bar.ReadOnly.Platform.OS != opts.os {
|
|
continue
|
|
}
|
|
// wrong disktype
|
|
if val != opts.diskType {
|
|
continue
|
|
}
|
|
|
|
// ok, we have a match
|
|
artifactDigest = d
|
|
logrus.Debugf("found image in digest: %q", artifactDigest.String())
|
|
break
|
|
}
|
|
if artifactDigest == "" {
|
|
return "", fmt.Errorf("no valid disk artifact found")
|
|
}
|
|
v1RawMannyfest, _, err := imgSrc.GetManifest(ctx, &artifactDigest)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
v1MannyFest := specV1.Manifest{}
|
|
if err := json.Unmarshal(v1RawMannyfest, &v1MannyFest); err != nil {
|
|
return "", err
|
|
}
|
|
if layerLen := len(v1MannyFest.Layers); layerLen > 1 {
|
|
return "", fmt.Errorf("podman-machine images should only have 1 layer: %d found", layerLen)
|
|
}
|
|
|
|
// podman-machine-images should have a original file name
|
|
// stored in the annotations under org.opencontainers.image.title
|
|
// i.e. fedora-coreos-39.20240128.2.2-qemu.x86_64.qcow2.xz
|
|
originalFileName, ok := v1MannyFest.Layers[0].Annotations["org.opencontainers.image.title"]
|
|
if !ok {
|
|
return "", fmt.Errorf("unable to determine original artifact name: missing required annotation 'org.opencontainers.image.title'")
|
|
}
|
|
logrus.Debugf("original artifact file name: %s", originalFileName)
|
|
return artifactDigest, err
|
|
}
|