mirror of
https://github.com/containers/podman.git
synced 2025-06-29 06:57:13 +08:00
remote: fix manifest add --annotation
* `manifest add --annotation option` adds annotations field on remote environment. * `manifest inspect` prints annotations field on remote environment. Fixes: #15952 Signed-off-by: Toshiki Sonoda <sonoda.toshiki@fujitsu.com>
This commit is contained in:
@ -11,8 +11,8 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/libimage"
|
||||||
"github.com/containers/image/v5/docker/reference"
|
"github.com/containers/image/v5/docker/reference"
|
||||||
"github.com/containers/image/v5/manifest"
|
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v4/libpod"
|
"github.com/containers/podman/v4/libpod"
|
||||||
"github.com/containers/podman/v4/pkg/api/handlers"
|
"github.com/containers/podman/v4/pkg/api/handlers"
|
||||||
@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/containers/podman/v4/pkg/channel"
|
"github.com/containers/podman/v4/pkg/channel"
|
||||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||||
"github.com/containers/podman/v4/pkg/domain/infra/abi"
|
"github.com/containers/podman/v4/pkg/domain/infra/abi"
|
||||||
|
envLib "github.com/containers/podman/v4/pkg/env"
|
||||||
"github.com/containers/podman/v4/pkg/errorhandling"
|
"github.com/containers/podman/v4/pkg/errorhandling"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/schema"
|
"github.com/gorilla/schema"
|
||||||
@ -164,7 +165,7 @@ func ManifestInspect(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var schema2List manifest.Schema2List
|
var schema2List libimage.ManifestListData
|
||||||
if err := json.Unmarshal(rawManifest, &schema2List); err != nil {
|
if err := json.Unmarshal(rawManifest, &schema2List); err != nil {
|
||||||
utils.Error(w, http.StatusInternalServerError, err)
|
utils.Error(w, http.StatusInternalServerError, err)
|
||||||
return
|
return
|
||||||
@ -460,6 +461,24 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(body.ManifestAddOptions.Annotation) != 0 {
|
||||||
|
if len(body.ManifestAddOptions.Annotations) != 0 {
|
||||||
|
utils.Error(w, http.StatusBadRequest, fmt.Errorf("can not set both Annotation and Annotations"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
annotations := make(map[string]string)
|
||||||
|
for _, annotationSpec := range body.ManifestAddOptions.Annotation {
|
||||||
|
spec := strings.SplitN(annotationSpec, "=", 2)
|
||||||
|
if len(spec) != 2 {
|
||||||
|
utils.Error(w, http.StatusBadRequest, fmt.Errorf("no value given for annotation %q", spec[0]))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
annotations[spec[0]] = spec[1]
|
||||||
|
}
|
||||||
|
body.ManifestAddOptions.Annotations = envLib.Join(body.ManifestAddOptions.Annotations, annotations)
|
||||||
|
body.ManifestAddOptions.Annotation = nil
|
||||||
|
}
|
||||||
|
|
||||||
if tlsVerify, ok := r.URL.Query()["tlsVerify"]; ok {
|
if tlsVerify, ok := r.URL.Query()["tlsVerify"]; ok {
|
||||||
tls, err := strconv.ParseBool(tlsVerify[len(tlsVerify)-1])
|
tls, err := strconv.ParseBool(tlsVerify[len(tlsVerify)-1])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/libimage"
|
||||||
"github.com/containers/image/v5/manifest"
|
"github.com/containers/image/v5/manifest"
|
||||||
imageTypes "github.com/containers/image/v5/types"
|
imageTypes "github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v4/pkg/auth"
|
"github.com/containers/podman/v4/pkg/auth"
|
||||||
@ -101,6 +102,39 @@ func Inspect(ctx context.Context, name string, options *InspectOptions) (*manife
|
|||||||
return &list, response.Process(&list)
|
return &list, response.Process(&list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InspectListData returns a manifest list for a given name.
|
||||||
|
// Contains exclusive field like `annotations` which is only
|
||||||
|
// present in OCI spec and not in docker image spec.
|
||||||
|
func InspectListData(ctx context.Context, name string, options *InspectOptions) (*libimage.ManifestListData, error) {
|
||||||
|
conn, err := bindings.GetClient(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if options == nil {
|
||||||
|
options = new(InspectOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
params, err := options.ToParams()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// SkipTLSVerify is special. We need to delete the param added by
|
||||||
|
// ToParams() and change the key and flip the bool
|
||||||
|
if options.SkipTLSVerify != nil {
|
||||||
|
params.Del("SkipTLSVerify")
|
||||||
|
params.Set("tlsVerify", strconv.FormatBool(!options.GetSkipTLSVerify()))
|
||||||
|
}
|
||||||
|
|
||||||
|
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/manifests/%s/json", params, nil, name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
var list libimage.ManifestListData
|
||||||
|
return &list, response.Process(&list)
|
||||||
|
}
|
||||||
|
|
||||||
// Add adds a manifest to a given manifest list. Additional options for the manifest
|
// Add adds a manifest to a given manifest list. Additional options for the manifest
|
||||||
// can also be specified. The ID of the new manifest list is returned as a string
|
// can also be specified. The ID of the new manifest list is returned as a string
|
||||||
func Add(ctx context.Context, name string, options *AddOptions) (string, error) {
|
func Add(ctx context.Context, name string, options *AddOptions) (string, error) {
|
||||||
|
@ -43,6 +43,8 @@ type ManifestAddOptions struct {
|
|||||||
type ManifestAnnotateOptions struct {
|
type ManifestAnnotateOptions struct {
|
||||||
// Annotation to add to manifest list
|
// Annotation to add to manifest list
|
||||||
Annotation []string `json:"annotation" schema:"annotation"`
|
Annotation []string `json:"annotation" schema:"annotation"`
|
||||||
|
// Annotations to add to manifest list by a map which is prefferred over Annotation
|
||||||
|
Annotations map[string]string `json:"annotations" schema:"annotations"`
|
||||||
// Arch overrides the architecture for the image
|
// Arch overrides the architecture for the image
|
||||||
Arch string `json:"arch" schema:"arch"`
|
Arch string `json:"arch" schema:"arch"`
|
||||||
// Feature list for the image
|
// Feature list for the image
|
||||||
|
@ -19,6 +19,7 @@ import (
|
|||||||
"github.com/containers/image/v5/transports/alltransports"
|
"github.com/containers/image/v5/transports/alltransports"
|
||||||
"github.com/containers/image/v5/types"
|
"github.com/containers/image/v5/types"
|
||||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||||
|
envLib "github.com/containers/podman/v4/pkg/env"
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||||
@ -231,8 +232,9 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, name string, images []st
|
|||||||
}
|
}
|
||||||
annotations[spec[0]] = spec[1]
|
annotations[spec[0]] = spec[1]
|
||||||
}
|
}
|
||||||
annotateOptions.Annotations = annotations
|
opts.Annotations = envLib.Join(opts.Annotations, annotations)
|
||||||
}
|
}
|
||||||
|
annotateOptions.Annotations = opts.Annotations
|
||||||
|
|
||||||
if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
|
if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@ -269,8 +271,9 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, name, image string,
|
|||||||
}
|
}
|
||||||
annotations[spec[0]] = spec[1]
|
annotations[spec[0]] = spec[1]
|
||||||
}
|
}
|
||||||
annotateOptions.Annotations = annotations
|
opts.Annotations = envLib.Join(opts.Annotations, annotations)
|
||||||
}
|
}
|
||||||
|
annotateOptions.Annotations = opts.Annotations
|
||||||
|
|
||||||
if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
|
if err := manifestList.AnnotateInstance(instanceDigest, annotateOptions); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/containers/podman/v4/pkg/bindings/images"
|
"github.com/containers/podman/v4/pkg/bindings/images"
|
||||||
"github.com/containers/podman/v4/pkg/bindings/manifests"
|
"github.com/containers/podman/v4/pkg/bindings/manifests"
|
||||||
"github.com/containers/podman/v4/pkg/domain/entities"
|
"github.com/containers/podman/v4/pkg/domain/entities"
|
||||||
|
envLib "github.com/containers/podman/v4/pkg/env"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ManifestCreate implements manifest create via ImageEngine
|
// ManifestCreate implements manifest create via ImageEngine
|
||||||
@ -43,7 +44,7 @@ func (ir *ImageEngine) ManifestInspect(ctx context.Context, name string, opts en
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
list, err := manifests.Inspect(ir.ClientCtx, name, options)
|
list, err := manifests.InspectListData(ir.ClientCtx, name, options)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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)
|
||||||
}
|
}
|
||||||
@ -60,6 +61,7 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames []
|
|||||||
options := new(manifests.AddOptions).WithAll(opts.All).WithArch(opts.Arch).WithVariant(opts.Variant)
|
options := new(manifests.AddOptions).WithAll(opts.All).WithArch(opts.Arch).WithVariant(opts.Variant)
|
||||||
options.WithFeatures(opts.Features).WithImages(imageNames).WithOS(opts.OS).WithOSVersion(opts.OSVersion)
|
options.WithFeatures(opts.Features).WithImages(imageNames).WithOS(opts.OS).WithOSVersion(opts.OSVersion)
|
||||||
options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile)
|
options.WithUsername(opts.Username).WithPassword(opts.Password).WithAuthfile(opts.Authfile)
|
||||||
|
|
||||||
if len(opts.Annotation) != 0 {
|
if len(opts.Annotation) != 0 {
|
||||||
annotations := make(map[string]string)
|
annotations := make(map[string]string)
|
||||||
for _, annotationSpec := range opts.Annotation {
|
for _, annotationSpec := range opts.Annotation {
|
||||||
@ -69,8 +71,10 @@ func (ir *ImageEngine) ManifestAdd(_ context.Context, name string, imageNames []
|
|||||||
}
|
}
|
||||||
annotations[spec[0]] = spec[1]
|
annotations[spec[0]] = spec[1]
|
||||||
}
|
}
|
||||||
options.WithAnnotation(annotations)
|
opts.Annotations = envLib.Join(opts.Annotations, annotations)
|
||||||
}
|
}
|
||||||
|
options.WithAnnotation(opts.Annotations)
|
||||||
|
|
||||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||||
if s == types.OptionalBoolTrue {
|
if s == types.OptionalBoolTrue {
|
||||||
options.WithSkipTLSVerify(true)
|
options.WithSkipTLSVerify(true)
|
||||||
|
@ -28,7 +28,16 @@ EOF
|
|||||||
)
|
)
|
||||||
|
|
||||||
t POST /v3.4.0/libpod/manifests/$id_abc/add images="[\"containers-storage:$id_abc_image\"]" 200
|
t POST /v3.4.0/libpod/manifests/$id_abc/add images="[\"containers-storage:$id_abc_image\"]" 200
|
||||||
t PUT /v4.0.0/libpod/manifests/$id_xyz operation='update' images="[\"containers-storage:$id_xyz_image\"]" 200
|
t PUT /v4.0.0/libpod/manifests/$id_xyz operation='update' images="[\"containers-storage:$id_xyz_image\"]" annotations="{\"foo\":\"bar\"}" annotation="[\"hoge=fuga\"]" 400 \
|
||||||
|
.cause='can not set both Annotation and Annotations'
|
||||||
|
|
||||||
|
t PUT /v4.0.0/libpod/manifests/$id_xyz operation='update' images="[\"containers-storage:$id_xyz_image\"]" annotations="{\"foo\":\"bar\"}" 200
|
||||||
|
t GET /v4.0.0/libpod/manifests/$id_xyz/json 200 \
|
||||||
|
.manifests[0].annotations.foo="bar"
|
||||||
|
|
||||||
|
t PUT /v4.0.0/libpod/manifests/$id_xyz operation='update' images="[\"containers-storage:$id_xyz_image\"]" annotation="[\"hoge=fuga\"]" 200
|
||||||
|
t GET /v4.0.0/libpod/manifests/$id_xyz/json 200 \
|
||||||
|
.manifests[0].annotations.hoge="fuga"
|
||||||
|
|
||||||
t POST "/v3.4.0/libpod/manifests/abc:latest/push?destination=localhost:$REGISTRY_PORT%2Fabc:latest&tlsVerify=false&all=true" 200
|
t POST "/v3.4.0/libpod/manifests/abc:latest/push?destination=localhost:$REGISTRY_PORT%2Fabc:latest&tlsVerify=false&all=true" 200
|
||||||
t POST "/v4.0.0/libpod/manifests/xyz:latest/registry/localhost:$REGISTRY_PORT%2Fxyz:latest?all=true" 400 \
|
t POST "/v4.0.0/libpod/manifests/xyz:latest/registry/localhost:$REGISTRY_PORT%2Fxyz:latest?all=true" 400 \
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
package integration
|
package integration
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/containers/common/libimage"
|
||||||
podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
|
podmanRegistry "github.com/containers/podman/v4/hack/podman-registry-go"
|
||||||
. "github.com/containers/podman/v4/test/utils"
|
. "github.com/containers/podman/v4/test/utils"
|
||||||
"github.com/containers/storage/pkg/archive"
|
"github.com/containers/storage/pkg/archive"
|
||||||
@ -165,6 +167,27 @@ var _ = Describe("Podman manifest", func() {
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("add --annotation", func() {
|
||||||
|
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
session = podmanTest.Podman([]string{"manifest", "add", "--annotation", "hoge", "foo", imageList})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(125))
|
||||||
|
Expect(session.ErrorToString()).To(ContainSubstring("no value given for annotation"))
|
||||||
|
session = podmanTest.Podman([]string{"manifest", "add", "--annotation", "hoge=fuga", "foo", imageList})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
session = podmanTest.Podman([]string{"manifest", "inspect", "foo"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
Expect(session).Should(Exit(0))
|
||||||
|
|
||||||
|
var inspect libimage.ManifestListData
|
||||||
|
err := json.Unmarshal(session.Out.Contents(), &inspect)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
Expect(inspect.Manifests[0].Annotations).To(Equal(map[string]string{"hoge": "fuga"}))
|
||||||
|
})
|
||||||
|
|
||||||
It("add --os", func() {
|
It("add --os", func() {
|
||||||
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
|
session := podmanTest.Podman([]string{"manifest", "create", "foo"})
|
||||||
session.WaitWithDefaultTimeout()
|
session.WaitWithDefaultTimeout()
|
||||||
|
Reference in New Issue
Block a user