Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

image prune: support removing external containers #11737

Merged
merged 1 commit into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/podman/images/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func init() {

flags := pruneCmd.Flags()
flags.BoolVarP(&pruneOpts.All, "all", "a", false, "Remove all images not in use by containers, not just dangling ones")
flags.BoolVarP(&pruneOpts.External, "external", "", false, "Remove images even when they are used by external containers (e.g., by build containers)")
flags.BoolVarP(&force, "force", "f", false, "Do not prompt for confirmation")

filterFlagName := "filter"
Expand Down
4 changes: 4 additions & 0 deletions docs/source/markdown/podman-image-prune.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ The image prune command does not prune cache images that only use layers that ar

Remove dangling images and images that have no associated containers.

#### **--external**

Remove images even when they are used by external containers (e.g., build containers).

#### **--filter**=*filters*

Provide filter values.
Expand Down
3 changes: 1 addition & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ require (
github.com/containernetworking/cni v1.0.1
github.com/containernetworking/plugins v1.0.1
github.com/containers/buildah v1.23.0
github.com/containers/common v0.46.0
github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.16.0
github.com/containers/ocicrypt v1.1.2
Expand Down Expand Up @@ -61,7 +61,6 @@ require (
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/uber/jaeger-client-go v2.29.1+incompatible
github.com/vbauerster/mpb/v6 v6.0.4
github.com/vbauerster/mpb/v7 v7.1.4 // indirect
github.com/vishvananda/netlink v1.1.1-0.20210330154013-f5de75959ad5
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,8 @@ github.com/containernetworking/plugins v1.0.1/go.mod h1:QHCfGpaTwYTbbH+nZXKVTxNB
github.com/containers/buildah v1.23.0 h1:qGIeSNOczUHzvnaaOS29HSMiYAjw6JgIXYksAyvqnLs=
github.com/containers/buildah v1.23.0/go.mod h1:K0iMKgy/MffkkgELBXhSXwTy2HTT6hM0X8qruDR1FwU=
github.com/containers/common v0.44.0/go.mod h1:7sdP4vmI5Bm6FPFxb3lvAh1Iktb6tiO1MzjUzhxdoGo=
github.com/containers/common v0.46.0 h1:95zB7kYBQJW+aK5xxZnaobCwoPyYOf85Y0yUx0E5aRg=
github.com/containers/common v0.46.0/go.mod h1:zxv7KjdYddSGoWuLUVp6eSb++Ow1zmSMB2jwxuNB4cU=
github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6 h1:DojkCc4a9f3WB25Fk0GDap1/OkKU9UmDLvPJyqw3TBc=
github.com/containers/common v0.46.1-0.20210928081721-32e20295f1c6/go.mod h1:L4+sJlqi+R7frlbiWBW0baPra/cH8u5ZYwbxkukw3Lk=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.16.0 h1:WQcNSzb7+ngS2cfynx0vUwhk+scpgiKlldVcsF8GPbI=
Expand Down
21 changes: 21 additions & 0 deletions libpod/runtime_img.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,27 @@ func (r *Runtime) RemoveContainersForImageCallback(ctx context.Context) libimage
}
}

// IsExternalContainerCallback returns a callback that be used in `libimage` to
// figure out whether a given container is an external one. A container is
// considered external if it is not present in libpod's database.
func (r *Runtime) IsExternalContainerCallback(_ context.Context) libimage.IsExternalContainerFunc {
// NOTE: pruning external containers is subject to race conditions
// (e.g., when a container gets removed). To address this and similar
// races, pruning had to happen inside c/storage. Containers has to be
// labelled with "podman/libpod" along with callbacks similar to
// libimage.
return func(idOrName string) (bool, error) {
_, err := r.LookupContainer(idOrName)
vrothberg marked this conversation as resolved.
Show resolved Hide resolved
if err == nil {
return false, nil
}
if errors.Is(err, define.ErrNoSuchCtr) {
return true, nil
}
return false, nil
}
}

// newBuildEvent creates a new event based on completion of a built image
func (r *Runtime) newImageBuildCompleteEvent(idOrName string) {
e := events.NewEvent(events.Build)
Expand Down
8 changes: 5 additions & 3 deletions pkg/api/handlers/libpod/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := r.Context().Value(api.DecoderKey).(*schema.Decoder)
query := struct {
All bool `schema:"all"`
All bool `schema:"all"`
External bool `schema:"external"`
}{
// override any golang type defaults
}
Expand Down Expand Up @@ -190,8 +191,9 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
imageEngine := abi.ImageEngine{Libpod: runtime}

pruneOptions := entities.ImagePruneOptions{
All: query.All,
Filter: libpodFilters,
All: query.All,
External: query.External,
Filter: libpodFilters,
}
imagePruneReports, err := imageEngine.Prune(r.Context(), pruneOptions)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions pkg/api/server/register_images.go
Original file line number Diff line number Diff line change
Expand Up @@ -1050,6 +1050,12 @@ func (s *APIServer) registerImagesHandlers(r *mux.Router) error {
// description: |
// Remove all images not in use by containers, not just dangling ones
// - in: query
// name: external
// default: false
// type: boolean
// description: |
// Remove images even when they are used by external containers (e.g, by build containers)
// - in: query
// name: filters
// type: string
// description: |
Expand Down
2 changes: 2 additions & 0 deletions pkg/bindings/images/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ type ExportOptions struct {
type PruneOptions struct {
// Prune all images
All *bool
// Prune images even when they're used by external containers
External *bool
// Filters to apply when pruning images
Filters map[string][]string
}
Expand Down
15 changes: 15 additions & 0 deletions pkg/bindings/images/types_prune_options.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions pkg/domain/entities/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,9 @@ type ImageListOptions struct {
}

type ImagePruneOptions struct {
All bool `json:"all" schema:"all"`
Filter []string `json:"filter" schema:"filter"`
All bool `json:"all" schema:"all"`
External bool `json:"external" schema:"external"`
Filter []string `json:"filter" schema:"filter"`
}

type ImageTagOptions struct{}
Expand Down
12 changes: 10 additions & 2 deletions pkg/domain/infra/abi/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,21 @@ func (ir *ImageEngine) Exists(_ context.Context, nameOrID string) (*entities.Boo

func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) {
pruneOptions := &libimage.RemoveImagesOptions{
Filters: append(opts.Filter, "containers=false", "readonly=false"),
WithSize: true,
RemoveContainerFunc: ir.Libpod.RemoveContainersForImageCallback(ctx),
IsExternalContainerFunc: ir.Libpod.IsExternalContainerCallback(ctx),
ExternalContainers: opts.External,
Filters: append(opts.Filter, "readonly=false"),
WithSize: true,
}

if !opts.All {
pruneOptions.Filters = append(pruneOptions.Filters, "dangling=true")
}
if opts.External {
pruneOptions.Filters = append(pruneOptions.Filters, "containers=external")
} else {
pruneOptions.Filters = append(pruneOptions.Filters, "containers=false")
}

var pruneReports []*reports.PruneReport

Expand Down
2 changes: 1 addition & 1 deletion pkg/domain/infra/tunnel/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOption
f := strings.Split(filter, "=")
filters[f[0]] = f[1:]
}
options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters)
options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External)
reports, err := images.Prune(ir.ClientCtx, options)
if err != nil {
return nil, err
Expand Down
24 changes: 19 additions & 5 deletions test/system/040-ps.bats
Original file line number Diff line number Diff line change
Expand Up @@ -92,24 +92,38 @@ load helpers
# Force a buildah timeout; this leaves a buildah container behind
PODMAN_TIMEOUT=5 run_podman 124 build -t thiswillneverexist - <<EOF
FROM $IMAGE
RUN touch /intermediate.image.to.be.pruned
RUN sleep 30
EOF

run_podman ps -a
is "${#lines[@]}" "1" "podman ps -a does not see buildah container"
is "${#lines[@]}" "1" "podman ps -a does not see buildah containers"

run_podman ps --external -a
is "${#lines[@]}" "2" "podman ps -a --external sees buildah container"
is "${#lines[@]}" "3" "podman ps -a --external sees buildah containers"
is "${lines[1]}" \
"[0-9a-f]\{12\} \+$IMAGE *buildah .* seconds ago .* storage .* ${PODMAN_TEST_IMAGE_NAME}-working-container" \
"podman ps --external"

cid="${lines[1]:0:12}"

# 'rm -a' should be a NOP
run_podman rm -a
run_podman ps --external -a
is "${#lines[@]}" "2" "podman ps -a --external sees buildah container"
is "${#lines[@]}" "3" "podman ps -a --external sees buildah containers"

# Cannot prune intermediate image as it's being used by a buildah
# container.
run_podman image prune -f
is "$output" "" "No image is pruned"

# --external for removing buildah containers.
run_podman image prune -f --external
is "${#lines[@]}" "1" "Image used by build container is pruned"

# One buildah container has been removed.
run_podman ps --external -a
is "${#lines[@]}" "2" "podman ps -a --external sees buildah containers"

cid="${lines[1]:0:12}"

# We can't rm it without -f, but podman should issue a helpful message
run_podman 2 rm "$cid"
Expand Down
2 changes: 1 addition & 1 deletion test/system/330-corrupt-images.bats
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ function _corrupt_image_test() {

# Run the requested command. Confirm it succeeds, with suitable warnings
run_podman $*
is "$output" ".*error determining parent of image.*ignoring the error" \
is "$output" ".*Failed to determine parent of image.*ignoring the error" \
"$* with missing $what_to_rm"

run_podman images -a --noheading
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 29 additions & 9 deletions vendor/github.com/containers/common/libimage/filters.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading