Skip to content

Commit

Permalink
Merge pull request #8831 from bblenard/issue-8658-system-prune-reclai…
Browse files Browse the repository at this point in the history
…med-space

Rework pruning to report reclaimed space
  • Loading branch information
openshift-merge-robot authored Jan 5, 2021
2 parents bc21fab + b90f7f9 commit b84b7c8
Show file tree
Hide file tree
Showing 36 changed files with 245 additions and 232 deletions.
13 changes: 10 additions & 3 deletions cmd/podman/system/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/containers/podman/v2/cmd/podman/validate"
"github.com/containers/podman/v2/pkg/domain/entities"
dfilters "github.com/containers/podman/v2/pkg/domain/filters"
"github.com/docker/go-units"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -90,7 +91,7 @@ Are you sure you want to continue? [y/N] `, volumeString)
return err
}
// Print container prune results
err = utils.PrintContainerPruneResults(response.ContainerPruneReport, true)
err = utils.PrintContainerPruneResults(response.ContainerPruneReports, true)
if err != nil {
return err
}
Expand All @@ -101,11 +102,17 @@ Are you sure you want to continue? [y/N] `, volumeString)
}
// Print Volume prune results
if pruneOptions.Volume {
err = utils.PrintVolumePruneResults(response.VolumePruneReport, true)
err = utils.PrintVolumePruneResults(response.VolumePruneReports, true)
if err != nil {
return err
}
}
// Print Images prune results
return utils.PrintImagePruneResults(response.ImagePruneReport, true)
err = utils.PrintImagePruneResults(response.ImagePruneReports, true)
if err != nil {
return err
}

fmt.Printf("Total reclaimed space: %s\n", units.HumanSize((float64)(response.ReclaimedSpace)))
return nil
}
35 changes: 17 additions & 18 deletions cmd/podman/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"os"

"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/entities/reports"
)

// IsDir returns true if the specified path refers to a directory.
Expand Down Expand Up @@ -41,21 +42,21 @@ func PrintPodPruneResults(podPruneReports []*entities.PodPruneReport, heading bo
return errs.PrintErrors()
}

func PrintContainerPruneResults(containerPruneReport *entities.ContainerPruneReport, heading bool) error {
func PrintContainerPruneResults(containerPruneReports []*reports.PruneReport, heading bool) error {
var errs OutputErrors
if heading && (len(containerPruneReport.ID) > 0 || len(containerPruneReport.Err) > 0) {
if heading && (len(containerPruneReports) > 0) {
fmt.Println("Deleted Containers")
}
for k := range containerPruneReport.ID {
fmt.Println(k)
}
for _, v := range containerPruneReport.Err {
errs = append(errs, v)
for _, v := range containerPruneReports {
fmt.Println(v.Id)
if v.Err != nil {
errs = append(errs, v.Err)
}
}
return errs.PrintErrors()
}

func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport, heading bool) error {
func PrintVolumePruneResults(volumePruneReport []*reports.PruneReport, heading bool) error {
var errs OutputErrors
if heading && len(volumePruneReport) > 0 {
fmt.Println("Deleted Volumes")
Expand All @@ -70,18 +71,16 @@ func PrintVolumePruneResults(volumePruneReport []*entities.VolumePruneReport, he
return errs.PrintErrors()
}

func PrintImagePruneResults(imagePruneReport *entities.ImagePruneReport, heading bool) error {
if heading && (len(imagePruneReport.Report.Id) > 0 || len(imagePruneReport.Report.Err) > 0) {
func PrintImagePruneResults(imagePruneReports []*reports.PruneReport, heading bool) error {
if heading {
fmt.Println("Deleted Images")
}
for _, i := range imagePruneReport.Report.Id {
fmt.Println(i)
}
for _, e := range imagePruneReport.Report.Err {
fmt.Fprint(os.Stderr, e.Error()+"\n")
}
if imagePruneReport.Size > 0 {
fmt.Fprintf(os.Stdout, "Size: %d\n", imagePruneReport.Size)
for _, r := range imagePruneReports {
fmt.Println(r.Id)
if r.Err != nil {
fmt.Fprint(os.Stderr, r.Err.Error()+"\n")
}
}

return nil
}
23 changes: 18 additions & 5 deletions libpod/image/prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

"github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/pkg/domain/entities/reports"
"github.com/containers/podman/v2/pkg/timetype"
"github.com/containers/storage"
"github.com/pkg/errors"
Expand Down Expand Up @@ -110,7 +111,8 @@ func (ir *Runtime) GetPruneImages(ctx context.Context, all bool, filterFuncs []I

// PruneImages prunes dangling and optionally all unused images from the local
// image store
func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]*reports.PruneReport, error) {
preports := make([]*reports.PruneReport, 0)
filterFuncs := make([]ImageFilter, 0, len(filter))
for _, f := range filter {
filterSplit := strings.SplitN(f, "=", 2)
Expand All @@ -125,7 +127,6 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) (
filterFuncs = append(filterFuncs, generatedFunc)
}

pruned := []string{}
prev := 0
for {
toPrune, err := ir.GetPruneImages(ctx, all, filterFuncs)
Expand All @@ -143,6 +144,13 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) (
if err != nil {
return nil, err
}
nameOrID := img.ID()
s, err := img.Size(ctx)
imgSize := *s
if err != nil {
logrus.Warnf("Failed to collect image size for: %s, %s", nameOrID, err)
imgSize = 0
}
if err := img.Remove(ctx, false); err != nil {
if errors.Cause(err) == storage.ErrImageUsedByContainer {
logrus.Warnf("Failed to prune image %s as it is in use: %v.\nA container associated with containers/storage (e.g., Buildah, CRI-O, etc.) maybe associated with this image.\nUsing the rmi command with the --force option will remove the container and image, but may cause failures for other dependent systems.", img.ID(), err)
Expand All @@ -151,13 +159,18 @@ func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) (
return nil, errors.Wrap(err, "failed to prune image")
}
defer img.newImageEvent(events.Prune)
nameOrID := img.ID()

if len(repotags) > 0 {
nameOrID = repotags[0]
}
pruned = append(pruned, nameOrID)

preports = append(preports, &reports.PruneReport{
Id: nameOrID,
Err: nil,
Size: uint64(imgSize),
})
}

}
return pruned, nil
return preports, nil
}
27 changes: 16 additions & 11 deletions libpod/runtime_ctr.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/libpod/shutdown"
"github.com/containers/podman/v2/pkg/cgroups"
"github.com/containers/podman/v2/pkg/domain/entities/reports"
"github.com/containers/podman/v2/pkg/rootless"
"github.com/containers/storage"
"github.com/containers/storage/pkg/stringid"
Expand Down Expand Up @@ -884,9 +885,8 @@ func (r *Runtime) GetExecSessionContainer(id string) (*Container, error) {

// PruneContainers removes stopped and exited containers from localstorage. A set of optional filters
// can be provided to be more granular.
func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int64, map[string]error, error) {
pruneErrors := make(map[string]error)
prunedContainers := make(map[string]int64)
func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) ([]*reports.PruneReport, error) {
preports := make([]*reports.PruneReport, 0)
// We add getting the exited and stopped containers via a filter
containerStateFilter := func(c *Container) bool {
if c.PodID() != "" {
Expand All @@ -906,23 +906,28 @@ func (r *Runtime) PruneContainers(filterFuncs []ContainerFilter) (map[string]int
filterFuncs = append(filterFuncs, containerStateFilter)
delContainers, err := r.GetContainers(filterFuncs...)
if err != nil {
return nil, nil, err
return nil, err
}
for _, c := range delContainers {
ctr := c
size, err := ctr.RWSize()
report := new(reports.PruneReport)
report.Id = c.ID()
report.Err = nil
report.Size = 0
size, err := c.RWSize()
if err != nil {
pruneErrors[ctr.ID()] = err
report.Err = err
preports = append(preports, report)
continue
}
err = r.RemoveContainer(context.Background(), ctr, false, false)
err = r.RemoveContainer(context.Background(), c, false, false)
if err != nil {
pruneErrors[ctr.ID()] = err
report.Err = err
} else {
prunedContainers[ctr.ID()] = size
report.Size = (uint64)(size)
}
preports = append(preports, report)
}
return prunedContainers, pruneErrors, nil
return preports, nil
}

// MountStorageContainer mounts the storage container's root filesystem
Expand Down
25 changes: 18 additions & 7 deletions libpod/runtime_volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

"github.com/containers/podman/v2/libpod/define"
"github.com/containers/podman/v2/libpod/events"
"github.com/containers/podman/v2/pkg/domain/entities/reports"
"github.com/pkg/errors"
)

Expand Down Expand Up @@ -133,22 +134,32 @@ func (r *Runtime) GetAllVolumes() ([]*Volume, error) {
}

// PruneVolumes removes unused volumes from the system
func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) (map[string]error, error) {
reports := make(map[string]error)
func (r *Runtime) PruneVolumes(ctx context.Context, filterFuncs []VolumeFilter) ([]*reports.PruneReport, error) {
preports := make([]*reports.PruneReport, 0)
vols, err := r.Volumes(filterFuncs...)
if err != nil {
return nil, err
}

for _, vol := range vols {
report := new(reports.PruneReport)
volSize, err := vol.Size()
if err != nil {
volSize = 0
}
report.Size = volSize
report.Id = vol.Name()
if err := r.RemoveVolume(ctx, vol, false); err != nil {
if errors.Cause(err) != define.ErrVolumeBeingUsed && errors.Cause(err) != define.ErrVolumeRemoved {
reports[vol.Name()] = err
report.Err = err
} else {
// We didn't remove the volume for some reason
continue
}
continue
} else {
vol.newVolumeEvent(events.Prune)
}
vol.newVolumeEvent(events.Prune)
reports[vol.Name()] = nil
preports = append(preports, report)
}
return reports, nil
return preports, nil
}
14 changes: 14 additions & 0 deletions libpod/volume.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package libpod

import (
"os"
"path/filepath"
"time"

"github.com/containers/podman/v2/libpod/define"
Expand Down Expand Up @@ -79,6 +81,18 @@ func (v *Volume) Name() string {
return v.config.Name
}

// Returns the size on disk of volume
func (v *Volume) Size() (uint64, error) {
var size uint64
err := filepath.Walk(v.config.MountPoint, func(path string, info os.FileInfo, err error) error {
if err == nil && !info.IsDir() {
size += (uint64)(info.Size())
}
return err
})
return size, err
}

// Driver retrieves the volume's driver.
func (v *Volume) Driver() string {
return v.config.Driver
Expand Down
30 changes: 5 additions & 25 deletions pkg/api/handlers/compat/containers_prune.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@ import (

"github.com/containers/podman/v2/libpod"
"github.com/containers/podman/v2/pkg/api/handlers/utils"
"github.com/containers/podman/v2/pkg/domain/entities"
"github.com/containers/podman/v2/pkg/domain/entities/reports"
"github.com/containers/podman/v2/pkg/domain/filters"
"github.com/docker/docker/api/types"
"github.com/gorilla/schema"
"github.com/pkg/errors"
)

func PruneContainers(w http.ResponseWriter, r *http.Request) {
var (
delContainers []string
space int64
)
runtime := r.Context().Value("runtime").(*libpod.Runtime)
decoder := r.Context().Value("decoder").(*schema.Decoder)

Expand Down Expand Up @@ -49,36 +44,21 @@ func PruneContainers(w http.ResponseWriter, r *http.Request) {
return
}

prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs)
report, err := runtime.PruneContainers(filterFuncs)
if err != nil {
utils.InternalServerError(w, err)
return
}
for ctrID, size := range prunedContainers {
if pruneErrors[ctrID] == nil {
space += size
delContainers = append(delContainers, ctrID)
}
}
report := types.ContainersPruneReport{
ContainersDeleted: delContainers,
SpaceReclaimed: uint64(space),
}
utils.WriteResponse(w, http.StatusOK, report)
}

func PruneContainersHelper(w http.ResponseWriter, r *http.Request, filterFuncs []libpod.ContainerFilter) (
*entities.ContainerPruneReport, error) {
[]*reports.PruneReport, error) {
runtime := r.Context().Value("runtime").(*libpod.Runtime)
prunedContainers, pruneErrors, err := runtime.PruneContainers(filterFuncs)
reports, err := runtime.PruneContainers(filterFuncs)
if err != nil {
utils.InternalServerError(w, err)
return nil, err
}

report := &entities.ContainerPruneReport{
Err: pruneErrors,
ID: prunedContainers,
}
return report, nil
return reports, nil
}
10 changes: 6 additions & 4 deletions pkg/api/handlers/compat/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,21 +99,23 @@ func PruneImages(w http.ResponseWriter, r *http.Request) {
filters = append(filters, fmt.Sprintf("%s=%s", k, val))
}
}
pruneCids, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters)
imagePruneReports, err := runtime.ImageRuntime().PruneImages(r.Context(), query.All, filters)
if err != nil {
utils.InternalServerError(w, err)
return
}
for _, p := range pruneCids {
reclaimedSpace := uint64(0)
for _, p := range imagePruneReports {
idr = append(idr, types.ImageDeleteResponseItem{
Deleted: p,
Deleted: p.Id,
})
reclaimedSpace = reclaimedSpace + p.Size
}

// FIXME/TODO to do this exactly correct, pruneimages needs to return idrs and space-reclaimed, then we are golden
ipr := types.ImagesPruneReport{
ImagesDeleted: idr,
SpaceReclaimed: 1, // TODO we cannot supply this right now
SpaceReclaimed: reclaimedSpace,
}
utils.WriteResponse(w, http.StatusOK, handlers.ImagesPruneReport{ImagesPruneReport: ipr})
}
Expand Down
4 changes: 2 additions & 2 deletions pkg/api/handlers/compat/volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,9 +269,9 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) {
return
}
prunedIds := make([]string, 0, len(pruned))
for k := range pruned {
for _, v := range pruned {
// XXX: This drops any pruning per-volume error messages on the floor
prunedIds = append(prunedIds, k)
prunedIds = append(prunedIds, v.Id)
}
pruneResponse := docker_api_types.VolumesPruneReport{
VolumesDeleted: prunedIds,
Expand Down
Loading

0 comments on commit b84b7c8

Please sign in to comment.