From a9a54eefab34b25628906a786d6c4ca302f743b1 Mon Sep 17 00:00:00 2001
From: Valentin Rothberg <rothberg@redhat.com>
Date: Fri, 24 Sep 2021 14:39:36 +0200
Subject: image prune: support removing external containers

Support removing external containers (e.g., build containers) during
image prune.

Fixes: #11472
Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
---
 pkg/api/handlers/libpod/images.go          |  8 +++++---
 pkg/api/server/register_images.go          |  6 ++++++
 pkg/bindings/images/types.go               |  2 ++
 pkg/bindings/images/types_prune_options.go | 15 +++++++++++++++
 pkg/domain/entities/images.go              |  5 +++--
 pkg/domain/infra/abi/images.go             | 12 ++++++++++--
 pkg/domain/infra/tunnel/images.go          |  2 +-
 7 files changed, 42 insertions(+), 8 deletions(-)

(limited to 'pkg')

diff --git a/pkg/api/handlers/libpod/images.go b/pkg/api/handlers/libpod/images.go
index 0023479ea..1c6cc917c 100644
--- a/pkg/api/handlers/libpod/images.go
+++ b/pkg/api/handlers/libpod/images.go
@@ -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
 	}
@@ -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 {
diff --git a/pkg/api/server/register_images.go b/pkg/api/server/register_images.go
index 5e0de7def..aa573eaa6 100644
--- a/pkg/api/server/register_images.go
+++ b/pkg/api/server/register_images.go
@@ -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: |
diff --git a/pkg/bindings/images/types.go b/pkg/bindings/images/types.go
index 6ff9f18ec..dc6bd91c3 100644
--- a/pkg/bindings/images/types.go
+++ b/pkg/bindings/images/types.go
@@ -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
 }
diff --git a/pkg/bindings/images/types_prune_options.go b/pkg/bindings/images/types_prune_options.go
index 77bef32e3..c9772045e 100644
--- a/pkg/bindings/images/types_prune_options.go
+++ b/pkg/bindings/images/types_prune_options.go
@@ -32,6 +32,21 @@ func (o *PruneOptions) GetAll() bool {
 	return *o.All
 }
 
+// WithExternal set field External to given value
+func (o *PruneOptions) WithExternal(value bool) *PruneOptions {
+	o.External = &value
+	return o
+}
+
+// GetExternal returns value of field External
+func (o *PruneOptions) GetExternal() bool {
+	if o.External == nil {
+		var z bool
+		return z
+	}
+	return *o.External
+}
+
 // WithFilters set field Filters to given value
 func (o *PruneOptions) WithFilters(value map[string][]string) *PruneOptions {
 	o.Filters = value
diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go
index 80d570764..2822b1ad7 100644
--- a/pkg/domain/entities/images.go
+++ b/pkg/domain/entities/images.go
@@ -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{}
diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go
index 98d668434..c06059205 100644
--- a/pkg/domain/infra/abi/images.go
+++ b/pkg/domain/infra/abi/images.go
@@ -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
 
diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go
index 282770613..d41a20348 100644
--- a/pkg/domain/infra/tunnel/images.go
+++ b/pkg/domain/infra/tunnel/images.go
@@ -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
-- 
cgit v1.2.3-54-g00ecf