mirror of
https://github.com/containers/podman.git
synced 2025-05-20 08:36:23 +08:00
Merge pull request #24678 from rhatdan/manifest
Add podman manifest rm --ignore
This commit is contained in:
@ -6,13 +6,15 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/podman/v5/cmd/podman/common"
|
"github.com/containers/podman/v5/cmd/podman/common"
|
||||||
"github.com/containers/podman/v5/cmd/podman/registry"
|
"github.com/containers/podman/v5/cmd/podman/registry"
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
"github.com/containers/podman/v5/pkg/errorhandling"
|
"github.com/containers/podman/v5/pkg/errorhandling"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
rmCmd = &cobra.Command{
|
rmOptions = entities.ImageRemoveOptions{}
|
||||||
Use: "rm LIST [LIST...]",
|
rmCmd = &cobra.Command{
|
||||||
|
Use: "rm [options] LIST [LIST...]",
|
||||||
Short: "Remove manifest list or image index from local storage",
|
Short: "Remove manifest list or image index from local storage",
|
||||||
Long: "Remove manifest list or image index from local storage.",
|
Long: "Remove manifest list or image index from local storage.",
|
||||||
RunE: rm,
|
RunE: rm,
|
||||||
@ -27,10 +29,13 @@ func init() {
|
|||||||
Command: rmCmd,
|
Command: rmCmd,
|
||||||
Parent: manifestCmd,
|
Parent: manifestCmd,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
flags := rmCmd.Flags()
|
||||||
|
flags.BoolVarP(&rmOptions.Ignore, "ignore", "i", false, "Ignore errors when a specified manifest is missing")
|
||||||
}
|
}
|
||||||
|
|
||||||
func rm(cmd *cobra.Command, args []string) error {
|
func rm(cmd *cobra.Command, args []string) error {
|
||||||
report, rmErrors := registry.ImageEngine().ManifestRm(context.Background(), args)
|
report, rmErrors := registry.ImageEngine().ManifestRm(context.Background(), args, rmOptions)
|
||||||
if report != nil {
|
if report != nil {
|
||||||
for _, u := range report.Untagged {
|
for _, u := range report.Untagged {
|
||||||
fmt.Println("Untagged: " + u)
|
fmt.Println("Untagged: " + u)
|
||||||
|
@ -4,11 +4,17 @@
|
|||||||
podman\-manifest\-rm - Remove manifest list or image index from local storage
|
podman\-manifest\-rm - Remove manifest list or image index from local storage
|
||||||
|
|
||||||
## SYNOPSIS
|
## SYNOPSIS
|
||||||
**podman manifest rm** *list-or-index* [...]
|
**podman manifest rm** [*options*] *list-or-index* [...]
|
||||||
|
|
||||||
## DESCRIPTION
|
## DESCRIPTION
|
||||||
Removes one or more locally stored manifest lists.
|
Removes one or more locally stored manifest lists.
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
|
||||||
|
#### **--ignore**, **-i**
|
||||||
|
|
||||||
|
If a specified manifest does not exist in the local storage, ignore it and do not throw an error.
|
||||||
|
|
||||||
## EXAMPLE
|
## EXAMPLE
|
||||||
|
|
||||||
podman manifest rm `<list>`
|
podman manifest rm `<list>`
|
||||||
|
@ -22,6 +22,7 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
|
|||||||
query := struct {
|
query := struct {
|
||||||
Force bool `schema:"force"`
|
Force bool `schema:"force"`
|
||||||
NoPrune bool `schema:"noprune"`
|
NoPrune bool `schema:"noprune"`
|
||||||
|
Ignore bool `schema:"ignore"`
|
||||||
}{
|
}{
|
||||||
// This is where you can override the golang default value for one of fields
|
// This is where you can override the golang default value for one of fields
|
||||||
}
|
}
|
||||||
@ -42,6 +43,7 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
|
|||||||
options := entities.ImageRemoveOptions{
|
options := entities.ImageRemoveOptions{
|
||||||
Force: query.Force,
|
Force: query.Force,
|
||||||
NoPrune: query.NoPrune,
|
NoPrune: query.NoPrune,
|
||||||
|
Ignore: query.Ignore,
|
||||||
}
|
}
|
||||||
report, rmerrors := imageEngine.Remove(r.Context(), []string{possiblyNormalizedName}, options)
|
report, rmerrors := imageEngine.Remove(r.Context(), []string{possiblyNormalizedName}, options)
|
||||||
if len(rmerrors) > 0 && rmerrors[0] != nil {
|
if len(rmerrors) > 0 && rmerrors[0] != nil {
|
||||||
|
@ -668,6 +668,7 @@ func ImagesRemove(w http.ResponseWriter, r *http.Request) {
|
|||||||
query := struct {
|
query := struct {
|
||||||
Force bool `schema:"force"`
|
Force bool `schema:"force"`
|
||||||
LookupManifest bool `schema:"lookupManifest"`
|
LookupManifest bool `schema:"lookupManifest"`
|
||||||
|
Ignore bool `schema:"ignore"`
|
||||||
}{
|
}{
|
||||||
Force: false,
|
Force: false,
|
||||||
}
|
}
|
||||||
@ -677,7 +678,7 @@ func ImagesRemove(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
opts := entities.ImageRemoveOptions{Force: query.Force, LookupManifest: query.LookupManifest}
|
opts := entities.ImageRemoveOptions{Force: query.Force, LookupManifest: query.LookupManifest, Ignore: query.Ignore}
|
||||||
imageEngine := abi.ImageEngine{Libpod: runtime}
|
imageEngine := abi.ImageEngine{Libpod: runtime}
|
||||||
rmReport, rmErrors := imageEngine.Remove(r.Context(), []string{utils.GetName(r)}, opts)
|
rmReport, rmErrors := imageEngine.Remove(r.Context(), []string{utils.GetName(r)}, opts)
|
||||||
|
|
||||||
|
@ -745,20 +745,43 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// ManifestDelete removes a manifest list from storage
|
// ManifestDelete removes a manifest list from storage
|
||||||
func ManifestDelete(w http.ResponseWriter, r *http.Request) {
|
func ManifestDelete(w http.ResponseWriter, r *http.Request) {
|
||||||
|
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
|
||||||
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
|
||||||
imageEngine := abi.ImageEngine{Libpod: runtime}
|
imageEngine := abi.ImageEngine{Libpod: runtime}
|
||||||
|
|
||||||
name := utils.GetName(r)
|
query := struct {
|
||||||
if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil {
|
Ignore bool `schema:"ignore"`
|
||||||
utils.Error(w, http.StatusNotFound, err)
|
}{
|
||||||
return
|
// Add defaults here once needed.
|
||||||
}
|
}
|
||||||
|
|
||||||
results, errs := imageEngine.ManifestRm(r.Context(), []string{name})
|
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||||
errsString := errorhandling.ErrorsToStrings(errs)
|
utils.Error(w, http.StatusBadRequest,
|
||||||
report := handlers.LibpodImagesRemoveReport{
|
fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
|
||||||
ImageRemoveReport: *results,
|
return
|
||||||
Errors: errsString,
|
}
|
||||||
|
opts := entities.ImageRemoveOptions{
|
||||||
|
Ignore: query.Ignore,
|
||||||
|
}
|
||||||
|
|
||||||
|
name := utils.GetName(r)
|
||||||
|
rmReport, rmErrors := imageEngine.ManifestRm(r.Context(), []string{name}, opts)
|
||||||
|
// In contrast to batch-removal, where we're only setting the exit
|
||||||
|
// code, we need to have another closer look at the errors here and set
|
||||||
|
// the appropriate http status code.
|
||||||
|
|
||||||
|
switch rmReport.ExitCode {
|
||||||
|
case 0:
|
||||||
|
report := handlers.LibpodImagesRemoveReport{ImageRemoveReport: *rmReport, Errors: []string{}}
|
||||||
|
utils.WriteResponse(w, http.StatusOK, report)
|
||||||
|
case 1:
|
||||||
|
// 404 - no such image
|
||||||
|
utils.Error(w, http.StatusNotFound, errorhandling.JoinErrors(rmErrors))
|
||||||
|
case 2:
|
||||||
|
// 409 - conflict error (in use by containers)
|
||||||
|
utils.Error(w, http.StatusConflict, errorhandling.JoinErrors(rmErrors))
|
||||||
|
default:
|
||||||
|
// 500 - internal error
|
||||||
|
utils.Error(w, http.StatusInternalServerError, errorhandling.JoinErrors(rmErrors))
|
||||||
}
|
}
|
||||||
utils.WriteResponse(w, http.StatusOK, report)
|
|
||||||
}
|
}
|
||||||
|
@ -322,6 +322,10 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
|
|||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
// description: The name or ID of the list to be deleted
|
// description: The name or ID of the list to be deleted
|
||||||
|
// - in: query
|
||||||
|
// name: ignore
|
||||||
|
// description: Ignore if a specified manifest does not exist and do not throw an error.
|
||||||
|
// type: boolean
|
||||||
// responses:
|
// responses:
|
||||||
// 200:
|
// 200:
|
||||||
// $ref: "#/responses/imagesRemoveResponseLibpod"
|
// $ref: "#/responses/imagesRemoveResponseLibpod"
|
||||||
|
@ -45,7 +45,7 @@ type ImageEngine interface { //nolint:interfacebloat
|
|||||||
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)
|
||||||
ManifestRemoveDigest(ctx context.Context, names, image string) (string, error)
|
ManifestRemoveDigest(ctx context.Context, names, image string) (string, error)
|
||||||
ManifestRm(ctx context.Context, names []string) (*ImageRemoveReport, []error)
|
ManifestRm(ctx context.Context, names []string, imageRmOpts ImageRemoveOptions) (*ImageRemoveReport, []error)
|
||||||
ManifestPush(ctx context.Context, name, destination string, imagePushOpts ImagePushOptions) (string, error)
|
ManifestPush(ctx context.Context, name, destination string, imagePushOpts ImagePushOptions) (string, error)
|
||||||
ManifestListClear(ctx context.Context, name string) (string, error)
|
ManifestListClear(ctx context.Context, name string) (string, error)
|
||||||
Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error)
|
Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error)
|
||||||
|
@ -460,8 +460,8 @@ func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name, image str
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ManifestRm removes the specified manifest list from storage
|
// ManifestRm removes the specified manifest list from storage
|
||||||
func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (report *entities.ImageRemoveReport, rmErrors []error) {
|
func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, rmErrors []error) {
|
||||||
return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true})
|
return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true, Ignore: opts.Ignore})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestPush pushes a manifest list or image index to the destination
|
// ManifestPush pushes a manifest list or image index to the destination
|
||||||
|
@ -178,8 +178,8 @@ func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name string, im
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ManifestRm removes the specified manifest list from storage
|
// ManifestRm removes the specified manifest list from storage
|
||||||
func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (*entities.ImageRemoveReport, []error) {
|
func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string, opts entities.ImageRemoveOptions) (report *entities.ImageRemoveReport, rmErrors []error) {
|
||||||
return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true})
|
return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true, Ignore: opts.Ignore})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ManifestPush pushes a manifest list or image index to the destination
|
// ManifestPush pushes a manifest list or image index to the destination
|
||||||
|
@ -66,6 +66,9 @@ t POST "/v4.0.0/libpod/manifests/xyz:latest/registry/localhost:$REGISTRY_PORT%2F
|
|||||||
# /v3.x cannot delete a manifest list
|
# /v3.x cannot delete a manifest list
|
||||||
t DELETE /v4.0.0/libpod/manifests/$id_abc 200
|
t DELETE /v4.0.0/libpod/manifests/$id_abc 200
|
||||||
t DELETE /v4.0.0/libpod/manifests/$id_xyz 200
|
t DELETE /v4.0.0/libpod/manifests/$id_xyz 200
|
||||||
|
t GET /v4.0.0/libpod/manifests/$id_xyz/exists 404
|
||||||
|
t DELETE /v4.0.0/libpod/manifests/$id_xyz 404
|
||||||
|
t DELETE /v4.0.0/libpod/manifests/$id_xyz?ignore=true 200
|
||||||
|
|
||||||
# manifest add --artifact tests
|
# manifest add --artifact tests
|
||||||
truncate -s 20M $WORKDIR/zeroes
|
truncate -s 20M $WORKDIR/zeroes
|
||||||
|
@ -94,6 +94,10 @@ function validate_instance_compression {
|
|||||||
--tls-verify=false $mid \
|
--tls-verify=false $mid \
|
||||||
$manifest1
|
$manifest1
|
||||||
run_podman manifest rm $manifest1
|
run_podman manifest rm $manifest1
|
||||||
|
run_podman 1 manifest rm $manifest1
|
||||||
|
is "$output" "Error: $manifest1: image not known" "Missing manifest is reported"
|
||||||
|
run_podman manifest rm --ignore $manifest1
|
||||||
|
is "$output" "" "Missing manifest is ignored"
|
||||||
|
|
||||||
# Default is to require TLS; also test explicit opts
|
# Default is to require TLS; also test explicit opts
|
||||||
for opt in '' '--insecure=false' '--tls-verify=true' "--authfile=$authfile"; do
|
for opt in '' '--insecure=false' '--tls-verify=true' "--authfile=$authfile"; do
|
||||||
|
Reference in New Issue
Block a user