mirror of
https://github.com/containers/podman.git
synced 2025-06-27 21:50:18 +08:00
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:
@ -1,6 +1,7 @@
|
|||||||
package manifest
|
package manifest
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/containers/common/pkg/auth"
|
"github.com/containers/common/pkg/auth"
|
||||||
@ -55,10 +56,14 @@ func inspect(cmd *cobra.Command, args []string) error {
|
|||||||
insecure, _ := cmd.Flags().GetBool("insecure")
|
insecure, _ := cmd.Flags().GetBool("insecure")
|
||||||
inspectOptions.SkipTLSVerify = types.NewOptionalBool(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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Println(string(buf))
|
prettyJSON, err := json.MarshalIndent(list, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(string(prettyJSON))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containers/common/libimage/define"
|
|
||||||
"github.com/containers/image/v5/docker/reference"
|
"github.com/containers/image/v5/docker/reference"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v5/libpod"
|
"github.com/containers/podman/v5/libpod"
|
||||||
@ -190,19 +189,13 @@ func ManifestInspect(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
imageEngine := abi.ImageEngine{Libpod: runtime}
|
imageEngine := abi.ImageEngine{Libpod: runtime}
|
||||||
rawManifest, err := imageEngine.ManifestInspect(r.Context(), name, opts)
|
manifest, err := imageEngine.ManifestInspect(r.Context(), name, opts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Error(w, http.StatusNotFound, err)
|
utils.Error(w, http.StatusNotFound, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var schema2List define.ManifestListData
|
utils.WriteResponse(w, http.StatusOK, manifest)
|
||||||
if err := json.Unmarshal(rawManifest, &schema2List); err != nil {
|
|
||||||
utils.Error(w, http.StatusInternalServerError, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.WriteResponse(w, http.StatusOK, schema2List)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestAddV3 remove digest from manifest list
|
// ManifestAddV3 remove digest from manifest list
|
||||||
|
@ -3,6 +3,7 @@ package entities
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/containers/common/libimage/define"
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/common/pkg/ssh"
|
"github.com/containers/common/pkg/ssh"
|
||||||
"github.com/containers/podman/v5/pkg/domain/entities/reports"
|
"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
|
Untag(ctx context.Context, nameOrID string, tags []string, options ImageUntagOptions) error
|
||||||
ManifestCreate(ctx context.Context, name string, images []string, opts ManifestCreateOptions) (string, error)
|
ManifestCreate(ctx context.Context, name string, images []string, opts ManifestCreateOptions) (string, error)
|
||||||
ManifestExists(ctx context.Context, name string) (*BoolReport, 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)
|
ManifestAdd(ctx context.Context, listName string, imageNames []string, opts ManifestAddOptions) (string, error)
|
||||||
ManifestAddArtifact(ctx context.Context, name string, files []string, opts ManifestAddArtifactOptions) (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)
|
ManifestAnnotate(ctx context.Context, names, image string, opts ManifestAnnotateOptions) (string, error)
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
@ -12,6 +11,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/common/libimage"
|
"github.com/containers/common/libimage"
|
||||||
|
"github.com/containers/common/libimage/define"
|
||||||
cp "github.com/containers/image/v5/copy"
|
cp "github.com/containers/image/v5/copy"
|
||||||
"github.com/containers/image/v5/docker"
|
"github.com/containers/image/v5/docker"
|
||||||
"github.com/containers/image/v5/manifest"
|
"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
|
// 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
|
// 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
|
// 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.
|
// 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
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
schema2List, err := manifestList.Inspect()
|
return 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// inspect a remote manifest list.
|
// 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()
|
sys := ir.Libpod.SystemContext()
|
||||||
|
|
||||||
if opts.Authfile != "" {
|
if opts.Authfile != "" {
|
||||||
@ -134,7 +121,6 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, o
|
|||||||
latestErr error
|
latestErr error
|
||||||
result []byte
|
result []byte
|
||||||
manType string
|
manType string
|
||||||
b bytes.Buffer
|
|
||||||
)
|
)
|
||||||
appendErr := func(e error) {
|
appendErr := func(e error) {
|
||||||
if latestErr == nil {
|
if latestErr == nil {
|
||||||
@ -181,27 +167,24 @@ func (ir *ImageEngine) remoteManifestInspect(ctx context.Context, name string, o
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing manifest blob %q as a %q: %w", string(result), manType, err)
|
return nil, fmt.Errorf("parsing manifest blob %q as a %q: %w", string(result), manType, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if result, err = schema2Manifest.Serialize(); err != nil {
|
if result, err = schema2Manifest.Serialize(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
listBlob, err := manifest.ListFromBlob(result, manType)
|
list, err := manifest.ListFromBlob(result, manType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("parsing manifest blob %q as a %q: %w", string(result), manType, err)
|
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 {
|
if result, err = list.Serialize(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = json.Indent(&b, result, "", " "); err != nil {
|
if err := json.Unmarshal(result, &inspectList); err != nil {
|
||||||
return nil, fmt.Errorf("rendering manifest %s for display: %w", name, err)
|
return nil, err
|
||||||
}
|
}
|
||||||
return b.Bytes(), nil
|
return &inspectList, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestAdd adds images to the manifest list
|
// ManifestAdd adds images to the manifest list
|
||||||
|
@ -2,11 +2,11 @@ package tunnel
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/libimage/define"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v5/pkg/bindings/images"
|
"github.com/containers/podman/v5/pkg/bindings/images"
|
||||||
"github.com/containers/podman/v5/pkg/bindings/manifests"
|
"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
|
// 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)
|
options := new(manifests.InspectOptions).WithAuthfile(opts.Authfile)
|
||||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||||
if s == types.OptionalBoolTrue {
|
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)
|
return nil, fmt.Errorf("getting content of manifest list or image %s: %w", name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf, err := json.MarshalIndent(list, "", " ")
|
return list, err
|
||||||
if err != nil {
|
|
||||||
return buf, fmt.Errorf("rendering manifest for display: %w", err)
|
|
||||||
}
|
|
||||||
return buf, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestAdd adds images to the manifest list
|
// ManifestAdd adds images to the manifest list
|
||||||
|
@ -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"
|
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"
|
skip_if_remote "running a local registry doesn't work with podman-remote"
|
||||||
|
|
||||||
# Using TARGETARCH gives us distinct images for each arch
|
# 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...
|
# Push to local registry; the magic key here is --add-compression...
|
||||||
local manifestpushed="localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$manifestlocal"
|
local manifestpushed="localhost:${PODMAN_LOGIN_REGISTRY_PORT}/$manifestlocal"
|
||||||
local authfile=${PODMAN_LOGIN_WORKDIR}/auth-manifest.json
|
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
|
# ...and use skopeo to confirm that each component has the right settings
|
||||||
echo "$_LOG_PROMPT skopeo inspect ... $manifestpushed"
|
echo "$_LOG_PROMPT skopeo inspect ... $manifestpushed"
|
||||||
list=$(skopeo inspect --authfile=$authfile --tls-verify=false --raw docker://$manifestpushed)
|
smanifest=$(skopeo inspect --authfile=$authfile --tls-verify=false --raw docker://$manifestpushed)
|
||||||
jq . <<<"$list"
|
jq . <<<"$smanifest"
|
||||||
|
|
||||||
validate_instance_compression "0" "$list" "amd64" "gzip"
|
validate_instance_compression "0" "$smanifest" "amd64" "gzip"
|
||||||
validate_instance_compression "1" "$list" "arm64" "gzip"
|
validate_instance_compression "1" "$smanifest" "arm64" "gzip"
|
||||||
validate_instance_compression "2" "$list" "amd64" "zstd"
|
validate_instance_compression "2" "$smanifest" "amd64" "zstd"
|
||||||
validate_instance_compression "3" "$list" "arm64" "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 rmi image_amd image_arm
|
||||||
run_podman manifest rm $manifestlocal
|
run_podman manifest rm $manifestlocal
|
||||||
|
Reference in New Issue
Block a user