github.com/containers/libpod@v1.9.4-0.20220419124438-4284fd425507/libpod/image/prune.go (about)

     1  package image
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"time"
     7  
     8  	"github.com/containers/libpod/libpod/events"
     9  	"github.com/containers/libpod/pkg/timetype"
    10  	"github.com/containers/storage"
    11  	"github.com/pkg/errors"
    12  	"github.com/sirupsen/logrus"
    13  )
    14  
    15  func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
    16  	switch filter {
    17  	case "label":
    18  		var filterArray = strings.SplitN(filterValue, "=", 2)
    19  		var filterKey = filterArray[0]
    20  		if len(filterArray) > 1 {
    21  			filterValue = filterArray[1]
    22  		} else {
    23  			filterValue = ""
    24  		}
    25  		return func(i *Image) bool {
    26  			labels, err := i.Labels(context.Background())
    27  			if err != nil {
    28  				return false
    29  			}
    30  			for labelKey, labelValue := range labels {
    31  				if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
    32  					return true
    33  				}
    34  			}
    35  			return false
    36  		}, nil
    37  
    38  	case "until":
    39  		ts, err := timetype.GetTimestamp(filterValue, time.Now())
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  		seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
    44  		if err != nil {
    45  			return nil, err
    46  		}
    47  		until := time.Unix(seconds, nanoseconds)
    48  		return func(i *Image) bool {
    49  			if !until.IsZero() && i.Created().After((until)) {
    50  				return true
    51  			}
    52  			return false
    53  		}, nil
    54  
    55  	}
    56  	return nil, nil
    57  }
    58  
    59  // GetPruneImages returns a slice of images that have no names/unused
    60  func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image, error) {
    61  	var (
    62  		pruneImages []*Image
    63  	)
    64  
    65  	allImages, err := ir.GetRWImages()
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	for _, i := range allImages {
    70  		// filter the images based on this.
    71  		for _, filterFunc := range filterFuncs {
    72  			if !filterFunc(i) {
    73  				continue
    74  			}
    75  		}
    76  
    77  		if len(i.Names()) == 0 {
    78  			pruneImages = append(pruneImages, i)
    79  			continue
    80  		}
    81  		if all {
    82  			containers, err := i.Containers()
    83  			if err != nil {
    84  				return nil, err
    85  			}
    86  			if len(containers) < 1 {
    87  				pruneImages = append(pruneImages, i)
    88  			}
    89  		}
    90  	}
    91  	return pruneImages, nil
    92  }
    93  
    94  // PruneImages prunes dangling and optionally all unused images from the local
    95  // image store
    96  func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
    97  	var (
    98  		prunedCids  []string
    99  		filterFuncs []ImageFilter
   100  	)
   101  	for _, f := range filter {
   102  		filterSplit := strings.SplitN(f, "=", 2)
   103  		if len(filterSplit) < 2 {
   104  			return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
   105  		}
   106  
   107  		generatedFunc, err := generatePruneFilterFuncs(filterSplit[0], filterSplit[1])
   108  		if err != nil {
   109  			return nil, errors.Wrapf(err, "invalid filter")
   110  		}
   111  		filterFuncs = append(filterFuncs, generatedFunc)
   112  	}
   113  
   114  	pruneImages, err := ir.GetPruneImages(all, filterFuncs)
   115  	if err != nil {
   116  		return nil, errors.Wrap(err, "unable to get images to prune")
   117  	}
   118  	for _, p := range pruneImages {
   119  		repotags, err := p.RepoTags()
   120  		if err != nil {
   121  			return nil, err
   122  		}
   123  		if err := p.Remove(ctx, true); err != nil {
   124  			if errors.Cause(err) == storage.ErrImageUsedByContainer {
   125  				logrus.Warnf("Failed to prune image %s as it is in use: %v", p.ID(), err)
   126  				continue
   127  			}
   128  			return nil, errors.Wrap(err, "failed to prune image")
   129  		}
   130  		defer p.newImageEvent(events.Prune)
   131  		nameOrID := p.ID()
   132  		if len(repotags) > 0 {
   133  			nameOrID = repotags[0]
   134  		}
   135  		prunedCids = append(prunedCids, nameOrID)
   136  	}
   137  	return prunedCids, nil
   138  }