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 }