Skip to content

Commit

Permalink
Merge pull request #24678 from rhatdan/manifest
Browse files Browse the repository at this point in the history
Add podman manifest rm --ignore
  • Loading branch information
openshift-merge-bot[bot] authored Jan 27, 2025
2 parents c76c13f + 5181bec commit 9403c3d
Show file tree
Hide file tree
Showing 11 changed files with 67 additions and 19 deletions.
11 changes: 8 additions & 3 deletions cmd/podman/manifest/rm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,15 @@ import (

"github.com/containers/podman/v5/cmd/podman/common"
"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/spf13/cobra"
)

var (
rmCmd = &cobra.Command{
Use: "rm LIST [LIST...]",
rmOptions = entities.ImageRemoveOptions{}
rmCmd = &cobra.Command{
Use: "rm [options] LIST [LIST...]",
Short: "Remove manifest list or image index from local storage",
Long: "Remove manifest list or image index from local storage.",
RunE: rm,
Expand All @@ -27,10 +29,13 @@ func init() {
Command: rmCmd,
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 {
report, rmErrors := registry.ImageEngine().ManifestRm(context.Background(), args)
report, rmErrors := registry.ImageEngine().ManifestRm(context.Background(), args, rmOptions)
if report != nil {
for _, u := range report.Untagged {
fmt.Println("Untagged: " + u)
Expand Down
8 changes: 7 additions & 1 deletion docs/source/markdown/podman-manifest-rm.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,17 @@
podman\-manifest\-rm - Remove manifest list or image index from local storage

## SYNOPSIS
**podman manifest rm** *list-or-index* [...]
**podman manifest rm** [*options*] *list-or-index* [...]

## DESCRIPTION
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

podman manifest rm `<list>`
Expand Down
2 changes: 2 additions & 0 deletions pkg/api/handlers/compat/images_remove.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
query := struct {
Force bool `schema:"force"`
NoPrune bool `schema:"noprune"`
Ignore bool `schema:"ignore"`
}{
// This is where you can override the golang default value for one of fields
}
Expand All @@ -42,6 +43,7 @@ func RemoveImage(w http.ResponseWriter, r *http.Request) {
options := entities.ImageRemoveOptions{
Force: query.Force,
NoPrune: query.NoPrune,
Ignore: query.Ignore,
}
report, rmerrors := imageEngine.Remove(r.Context(), []string{possiblyNormalizedName}, options)
if len(rmerrors) > 0 && rmerrors[0] != nil {
Expand Down
3 changes: 2 additions & 1 deletion pkg/api/handlers/libpod/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -668,6 +668,7 @@ func ImagesRemove(w http.ResponseWriter, r *http.Request) {
query := struct {
Force bool `schema:"force"`
LookupManifest bool `schema:"lookupManifest"`
Ignore bool `schema:"ignore"`
}{
Force: false,
}
Expand All @@ -677,7 +678,7 @@ func ImagesRemove(w http.ResponseWriter, r *http.Request) {
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}
rmReport, rmErrors := imageEngine.Remove(r.Context(), []string{utils.GetName(r)}, opts)

Expand Down
41 changes: 32 additions & 9 deletions pkg/api/handlers/libpod/manifests.go
Original file line number Diff line number Diff line change
Expand Up @@ -745,20 +745,43 @@ func ManifestModify(w http.ResponseWriter, r *http.Request) {

// ManifestDelete removes a manifest list from storage
func ManifestDelete(w http.ResponseWriter, r *http.Request) {
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
imageEngine := abi.ImageEngine{Libpod: runtime}

name := utils.GetName(r)
if _, err := runtime.LibimageRuntime().LookupManifestList(name); err != nil {
utils.Error(w, http.StatusNotFound, err)
query := struct {
Ignore bool `schema:"ignore"`
}{
// Add defaults here once needed.
}

if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusBadRequest,
fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
return
}
opts := entities.ImageRemoveOptions{
Ignore: query.Ignore,
}

results, errs := imageEngine.ManifestRm(r.Context(), []string{name})
errsString := errorhandling.ErrorsToStrings(errs)
report := handlers.LibpodImagesRemoveReport{
ImageRemoveReport: *results,
Errors: errsString,
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)
}
4 changes: 4 additions & 0 deletions pkg/api/server/register_manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,10 @@ func (s *APIServer) registerManifestHandlers(r *mux.Router) error {
// type: string
// required: true
// 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:
// 200:
// $ref: "#/responses/imagesRemoveResponseLibpod"
Expand Down
2 changes: 1 addition & 1 deletion pkg/domain/entities/engine_image.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ type ImageEngine interface { //nolint:interfacebloat
ManifestAddArtifact(ctx context.Context, name string, files []string, opts ManifestAddArtifactOptions) (string, error)
ManifestAnnotate(ctx context.Context, names, image string, opts ManifestAnnotateOptions) (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)
ManifestListClear(ctx context.Context, name string) (string, error)
Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error)
Expand Down
4 changes: 2 additions & 2 deletions pkg/domain/infra/abi/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,8 +460,8 @@ func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name, image str
}

// ManifestRm removes the specified manifest list from storage
func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (report *entities.ImageRemoveReport, rmErrors []error) {
return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true})
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, Ignore: opts.Ignore})
}

// ManifestPush pushes a manifest list or image index to the destination
Expand Down
4 changes: 2 additions & 2 deletions pkg/domain/infra/tunnel/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ func (ir *ImageEngine) ManifestRemoveDigest(ctx context.Context, name string, im
}

// ManifestRm removes the specified manifest list from storage
func (ir *ImageEngine) ManifestRm(ctx context.Context, names []string) (*entities.ImageRemoveReport, []error) {
return ir.Remove(ctx, names, entities.ImageRemoveOptions{LookupManifest: true})
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, Ignore: opts.Ignore})
}

// ManifestPush pushes a manifest list or image index to the destination
Expand Down
3 changes: 3 additions & 0 deletions test/apiv2/15-manifest.at
Original file line number Diff line number Diff line change
Expand Up @@ -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
t DELETE /v4.0.0/libpod/manifests/$id_abc 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
truncate -s 20M $WORKDIR/zeroes
Expand Down
4 changes: 4 additions & 0 deletions test/system/012-manifest.bats
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ function validate_instance_compression {
--tls-verify=false $mid \
$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
for opt in '' '--insecure=false' '--tls-verify=true' "--authfile=$authfile"; do
Expand Down

0 comments on commit 9403c3d

Please sign in to comment.