github.com/containers/podman/v2@v2.2.2-0.20210501105131-c1e07d070c4c/libpod/image/prune.go (about) 1 package image 2 3 import ( 4 "context" 5 "strings" 6 "time" 7 8 "github.com/containers/podman/v2/libpod/events" 9 "github.com/containers/podman/v2/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(ctx context.Context, 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 70 tree, err := ir.layerTree() 71 if err != nil { 72 return nil, err 73 } 74 75 for _, i := range allImages { 76 // filter the images based on this. 77 for _, filterFunc := range filterFuncs { 78 if !filterFunc(i) { 79 continue 80 } 81 } 82 83 if all { 84 containers, err := i.Containers() 85 if err != nil { 86 return nil, err 87 } 88 if len(containers) < 1 { 89 pruneImages = append(pruneImages, i) 90 continue 91 } 92 } 93 94 // skip the cache (i.e., with parent) and intermediate (i.e., 95 // with children) images 96 intermediate, err := tree.hasChildrenAndParent(ctx, i) 97 if err != nil { 98 return nil, err 99 } 100 if intermediate { 101 continue 102 } 103 104 if i.Dangling() { 105 pruneImages = append(pruneImages, i) 106 } 107 } 108 return pruneImages, nil 109 } 110 111 // PruneImages prunes dangling and optionally all unused images from the local 112 // image store 113 func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) { 114 filterFuncs := make([]ImageFilter, 0, len(filter)) 115 for _, f := range filter { 116 filterSplit := strings.SplitN(f, "=", 2) 117 if len(filterSplit) < 2 { 118 return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f) 119 } 120 121 generatedFunc, err := generatePruneFilterFuncs(filterSplit[0], filterSplit[1]) 122 if err != nil { 123 return nil, errors.Wrapf(err, "invalid filter") 124 } 125 filterFuncs = append(filterFuncs, generatedFunc) 126 } 127 128 pruned := []string{} 129 prev := 0 130 for { 131 toPrune, err := ir.GetPruneImages(ctx, all, filterFuncs) 132 if err != nil { 133 return nil, errors.Wrap(err, "unable to get images to prune") 134 } 135 numImages := len(toPrune) 136 if numImages == 0 || numImages == prev { 137 // If there's nothing left to do, return. 138 break 139 } 140 prev = numImages 141 for _, img := range toPrune { 142 repotags, err := img.RepoTags() 143 if err != nil { 144 return nil, err 145 } 146 if err := img.Remove(ctx, false); err != nil { 147 if errors.Cause(err) == storage.ErrImageUsedByContainer { 148 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) 149 continue 150 } 151 return nil, errors.Wrap(err, "failed to prune image") 152 } 153 defer img.newImageEvent(events.Prune) 154 nameOrID := img.ID() 155 if len(repotags) > 0 { 156 nameOrID = repotags[0] 157 } 158 pruned = append(pruned, nameOrID) 159 } 160 161 } 162 return pruned, nil 163 }