Should not force conversion of manifest type to DockerV2ListMediaType

Fixes: https://github.com/containers/podman/issues/23163

Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
This commit is contained in:
Daniel J Walsh
2024-08-07 06:05:11 -04:00
parent 61f7db5e7a
commit a06a7d7ba8
6 changed files with 55 additions and 54 deletions

View File

@ -1,6 +1,7 @@
package manifest
import (
"encoding/json"
"fmt"
"github.com/containers/common/pkg/auth"
@ -55,10 +56,14 @@ func inspect(cmd *cobra.Command, args []string) error {
insecure, _ := cmd.Flags().GetBool("insecure")
inspectOptions.SkipTLSVerify = types.NewOptionalBool(insecure)
}
buf, err := registry.ImageEngine().ManifestInspect(registry.Context(), args[0], inspectOptions)
list, err := registry.ImageEngine().ManifestInspect(registry.Context(), args[0], inspectOptions)
if err != nil {
return err
}
fmt.Println(string(buf))
prettyJSON, err := json.MarshalIndent(list, "", " ")
if err != nil {
return err
}
fmt.Println(string(prettyJSON))
return nil
}

View File

@ -15,7 +15,6 @@ import (
"strings"
"sync"
"github.com/containers/common/libimage/define"
"github.com/containers/image/v5/docker/reference"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v5/libpod"
@ -190,19 +189,13 @@ func ManifestInspect(w http.ResponseWriter, r *http.Request) {
}
imageEngine := abi.ImageEngine{Libpod: runtime}
rawManifest, err := imageEngine.ManifestInspect(r.Context(), name, opts)
manifest, err := imageEngine.ManifestInspect(r.Context(), name, opts)
if err != nil {
utils.Error(w, http.StatusNotFound, err)
return
}
var schema2List define.ManifestListData
if err := json.Unmarshal(rawManifest, &schema2List); err != nil {
utils.Error(w, http.StatusInternalServerError, err)
return
}
utils.WriteResponse(w, http.StatusOK, schema2List)
utils.WriteResponse(w, http.StatusOK, manifest)
}
// ManifestAddV3 remove digest from manifest list

View File

@ -3,6 +3,7 @@ package entities
import (
"context"
"github.com/containers/common/libimage/define"
"github.com/containers/common/pkg/config"
"github.com/containers/common/pkg/ssh"
"github.com/containers/podman/v5/pkg/domain/entities/reports"
@ -34,7 +35,7 @@ type ImageEngine interface { //nolint:interfacebloat
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
ManifestCreate(ctx context.Context, name string, images []string, opts ManifestCreateOptions) (string, error)
ManifestExists(ctx context.Context, name string) (*BoolReport, error)
ManifestInspect(ctx context.Context, name string, opts ManifestInspectOptions) ([]byte, error)
ManifestInspect(ctx context.Context, name string, opts ManifestInspectOptions) (*define.ManifestListData, error)
ManifestAdd(ctx context.Context, listName string, imageNames []string, opts ManifestAddOptions) (string, error)
ManifestAddArtifact(ctx context.Context, name string, files []string, opts ManifestAddArtifactOptions) (string, error)
ManifestAnnotate(ctx context.Context, names, image string, opts ManifestAnnotateOptions) (string, error)

View File

@ -1,7 +1,6 @@
package abi
import (
"bytes"
"context"
"encoding/json"
"errors"
@ -12,6 +11,7 @@ import (
"strings"
"github.com/containers/common/libimage"
"github.com/containers/common/libimage/define"
cp "github.com/containers/image/v5/copy"
"github.com/containers/image/v5/docker"
"github.com/containers/image/v5/manifest"
@ -79,7 +79,7 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti
}
// ManifestInspect returns the content of a manifest list or image
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) {
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) (*define.ManifestListData, error) {
// NOTE: we have to do a bit of a limbo here as `podman manifest
// inspect foo` wants to do a remote-inspect of foo iff "foo" in the
// containers storage is an ordinary image but not a manifest list.
@ -95,25 +95,12 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts en
return nil, err
}
schema2List, err := manifestList.Inspect()
if err != nil {
return nil, err
}
rawSchema2List, err := json.Marshal(schema2List)
if err != nil {
return nil, err
}
var b bytes.Buffer
if err := json.Indent(&b, rawSchema2List, "", " "); err != nil {
return nil, fmt.Errorf("rendering manifest %s for display: %w", name, err)
}
return b.Bytes(), nil
return manifestList.Inspect()
}
// inspect a remote manifest list.
func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) {
func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) (*define.ManifestListData, error) {
inspectList := define.ManifestListData{}
sys := ir.Libpod.SystemContext()
if opts.Authfile != "" {
@ -134,7 +121,6 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, o
latestErr error
result []byte
manType string
b bytes.Buffer
)
appendErr := func(e error) {
if latestErr == nil {
@ -181,27 +167,24 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, o
if err != nil {
return nil, fmt.Errorf("parsing manifest blob %q as a %q: %w", string(result), manType, err)
}
if result, err = schema2Manifest.Serialize(); err != nil {
return nil, err
}
default:
listBlob, err := manifest.ListFromBlob(result, manType)
list, err := manifest.ListFromBlob(result, manType)
if err != nil {
return nil, fmt.Errorf("parsing manifest blob %q as a %q: %w", string(result), manType, err)
}
list, err := listBlob.ConvertToMIMEType(manifest.DockerV2ListMediaType)
if err != nil {
return nil, err
}
if result, err = list.Serialize(); err != nil {
return nil, err
}
}
if err = json.Indent(&b, result, "", " "); err != nil {
return nil, fmt.Errorf("rendering manifest %s for display: %w", name, err)
if err := json.Unmarshal(result, &inspectList); err != nil {
return nil, err
}
return b.Bytes(), nil
return &inspectList, nil
}
// ManifestAdd adds images to the manifest list

View File

@ -2,11 +2,11 @@ package tunnel
import (
"context"
"encoding/json"
"fmt"
"slices"
"strings"
"github.com/containers/common/libimage/define"
"github.com/containers/image/v5/types"
"github.com/containers/podman/v5/pkg/bindings/images"
"github.com/containers/podman/v5/pkg/bindings/manifests"
@ -34,7 +34,7 @@ func (ir *ImageEngine) ManifestExists(ctx context.Context, name string) (*entiti
}
// ManifestInspect returns contents of manifest list with given name
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) ([]byte, error) {
func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts entities.ManifestInspectOptions) (*define.ManifestListData, error) {
options := new(manifests.InspectOptions).WithAuthfile(opts.Authfile)
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
if s == types.OptionalBoolTrue {
@ -49,11 +49,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts en
return nil, fmt.Errorf("getting content of manifest list or image %s: %w", name, err)
}
buf, err := json.MarshalIndent(list, "", " ")
if err != nil {
return buf, fmt.Errorf("rendering manifest for display: %w", err)
}
return buf, err
return list, err
}
// ManifestAdd adds images to the manifest list

View File

@ -107,7 +107,7 @@ function validate_instance_compression {
is "$output" ".*\"mediaType\": \"application/vnd.docker.distribution.manifest.list.v2+json\"" "Verify --tls-verify=false with REGISTRY_AUTH_FILE works against an insecure registry"
}
@test "manifest list --add-compression with zstd" {
@test "manifest list --add-compression with zstd:chunked" {
skip_if_remote "running a local registry doesn't work with podman-remote"
# Using TARGETARCH gives us distinct images for each arch
@ -133,17 +133,40 @@ EOF
# Push to local registry; the magic key here is --add-compression...
local manifestpushed="localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$manifestlocal"
local authfile=${PODMAN_LOGIN_WORKDIR}/auth-manifest.json
run_podman manifest push --authfile=$authfile --all --compression-format gzip --add-compression zstd --tls-verify=false $manifestlocal $manifestpushed
run_podman manifest push --authfile=$authfile --all --compression-format gzip --add-compression zstd:chunked --tls-verify=false $manifestlocal $manifestpushed
# ...and use skopeo to confirm that each component has the right settings
echo "$_LOG_PROMPT skopeo inspect ... $manifestpushed"
list=$(skopeo inspect --authfile=$authfile --tls-verify=false --raw docker://$manifestpushed)
jq . <<<"$list"
smanifest=$(skopeo inspect --authfile=$authfile --tls-verify=false --raw docker://$manifestpushed)
jq . <<<"$smanifest"
validate_instance_compression "0" "$list" "amd64" "gzip"
validate_instance_compression "1" "$list" "arm64" "gzip"
validate_instance_compression "2" "$list" "amd64" "zstd"
validate_instance_compression "3" "$list" "arm64" "zstd"
validate_instance_compression "0" "$smanifest" "amd64" "gzip"
validate_instance_compression "1" "$smanifest" "arm64" "gzip"
validate_instance_compression "2" "$smanifest" "amd64" "zstd"
validate_instance_compression "3" "$smanifest" "arm64" "zstd"
run_podman manifest inspect --authfile=$authfile --tls-verify=false \
$manifestpushed
pmanifest="$output"
objects=$(for obj in 0 1 2 3; do echo \
".manifests[$obj].annotations" \
".manifests[$obj].digest" \
".manifests[$obj].platform.architecture" \
".manifests[$obj].platform.os" \
".manifests[$obj].mediaType" \
".manifests[$obj].size" \
; done)
for object in \
$objects \
'.schemaVersion' \
'.mediaType' \
; do
skopeoObj=$(jq -r "$object" <<<"$smanifest")
podmanObj=$(jq -r "$object" <<<"$pmanifest")
assert "$skopeoObj" != "$smanifest" "\"$object\" does not exist in skopeo result"
assert "$podmanObj" == "$skopeoObj" "podman \"$object\" does not match skopeo"
done
run_podman rmi image_amd image_arm
run_podman manifest rm $manifestlocal