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

Rework pruning to report reclaimed space #8831

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