mirror of
https://github.com/containers/podman.git
synced 2025-08-06 03:19:52 +08:00
Merge pull request #4512 from kunalkushwaha/prune-filter
image prune command fixed as per docker image prune.
This commit is contained in:
4
API.md
4
API.md
@ -95,7 +95,7 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
|
||||
|
||||
[func ImageSave(options: ImageSaveOptions) MoreResponse](#ImageSave)
|
||||
|
||||
[func ImagesPrune(all: bool) []string](#ImagesPrune)
|
||||
[func ImagesPrune(all: bool, filter: []string) []string](#ImagesPrune)
|
||||
|
||||
[func ImportImage(source: string, reference: string, message: string, changes: []string, delete: bool) string](#ImportImage)
|
||||
|
||||
@ -766,7 +766,7 @@ ImageSave allows you to save an image from the local image storage to a tarball
|
||||
### <a name="ImagesPrune"></a>func ImagesPrune
|
||||
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
|
||||
|
||||
method ImagesPrune(all: [bool](https://godoc.org/builtin#bool)) [[]string](#[]string)</div>
|
||||
method ImagesPrune(all: [bool](https://godoc.org/builtin#bool), filter: [[]string](#[]string)) [[]string](#[]string)</div>
|
||||
ImagesPrune removes all unused images from the local store. Upon successful pruning,
|
||||
the IDs of the removed images are returned.
|
||||
### <a name="ImportImage"></a>func ImportImage
|
||||
|
@ -175,7 +175,9 @@ type HistoryValues struct {
|
||||
}
|
||||
type PruneImagesValues struct {
|
||||
PodmanCommand
|
||||
All bool
|
||||
All bool
|
||||
Force bool
|
||||
Filter []string
|
||||
}
|
||||
|
||||
type PruneContainersValues struct {
|
||||
|
@ -1,7 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/cliconfig"
|
||||
"github.com/containers/libpod/pkg/adapter"
|
||||
@ -34,9 +37,24 @@ func init() {
|
||||
pruneImagesCommand.SetUsageTemplate(UsageTemplate())
|
||||
flags := pruneImagesCommand.Flags()
|
||||
flags.BoolVarP(&pruneImagesCommand.All, "all", "a", false, "Remove all unused images, not just dangling ones")
|
||||
flags.BoolVarP(&pruneImagesCommand.Force, "force", "f", false, "Do not prompt for confirmation")
|
||||
flags.StringArrayVar(&pruneImagesCommand.Filter, "filter", []string{}, "Provide filter values (e.g. 'label=<key>=<value>')")
|
||||
}
|
||||
|
||||
func pruneImagesCmd(c *cliconfig.PruneImagesValues) error {
|
||||
if !c.Force {
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
fmt.Printf(`
|
||||
WARNING! This will remove all dangling images.
|
||||
Are you sure you want to continue? [y/N] `)
|
||||
ans, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "error reading input")
|
||||
}
|
||||
if strings.ToLower(ans)[0] != 'y' {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
runtime, err := adapter.GetRuntime(getContext(), &c.PodmanCommand)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "could not get runtime")
|
||||
@ -45,7 +63,7 @@ func pruneImagesCmd(c *cliconfig.PruneImagesValues) error {
|
||||
|
||||
// Call prune; if any cids are returned, print them and then
|
||||
// return err in case an error also came up
|
||||
pruneCids, err := runtime.PruneImages(getContext(), c.All)
|
||||
pruneCids, err := runtime.PruneImages(getContext(), c.All, c.Filter)
|
||||
if len(pruneCids) > 0 {
|
||||
for _, cid := range pruneCids {
|
||||
fmt.Println(cid)
|
||||
|
@ -117,7 +117,8 @@ Are you sure you want to continue? [y/N] `, volumeString)
|
||||
|
||||
// Call prune; if any cids are returned, print them and then
|
||||
// return err in case an error also came up
|
||||
pruneCids, err := runtime.PruneImages(ctx, c.All)
|
||||
// TODO: support for filters in system prune
|
||||
pruneCids, err := runtime.PruneImages(ctx, c.All, []string{})
|
||||
if len(pruneCids) > 0 {
|
||||
fmt.Println("Deleted Images")
|
||||
for _, cid := range pruneCids {
|
||||
|
@ -1217,7 +1217,7 @@ method UnmountContainer(name: string, force: bool) -> ()
|
||||
|
||||
# ImagesPrune removes all unused images from the local store. Upon successful pruning,
|
||||
# the IDs of the removed images are returned.
|
||||
method ImagesPrune(all: bool) -> (pruned: []string)
|
||||
method ImagesPrune(all: bool, filter: []string) -> (pruned: []string)
|
||||
|
||||
# This function is not implemented yet.
|
||||
# method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
|
||||
|
@ -25,13 +25,16 @@ Print usage statement
|
||||
Remove all dangling images from local storage
|
||||
```
|
||||
$ sudo podman image prune
|
||||
|
||||
WARNING! This will remove all dangling images.
|
||||
Are you sure you want to continue? [y/N] y
|
||||
f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e
|
||||
324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907
|
||||
```
|
||||
|
||||
Remove all unused images from local storage
|
||||
Remove all unused images from local storage without confirming
|
||||
```
|
||||
$ sudo podman image prune -a
|
||||
$ sudo podman image prune -a -f
|
||||
f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e
|
||||
324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907
|
||||
6125002719feb1ddf3030acab1df6156da7ce0e78e571e9b6e9c250424d6220c
|
||||
@ -41,6 +44,40 @@ e4e5109420323221f170627c138817770fb64832da7d8fe2babd863148287fca
|
||||
|
||||
```
|
||||
|
||||
Remove all unused images from local storage since given time/hours.
|
||||
```
|
||||
$ sudo podman image prune -a --filter until=2019-11-14T06:15:42.937792374Z
|
||||
|
||||
WARNING! This will remove all dangling images.
|
||||
Are you sure you want to continue? [y/N] y
|
||||
e813d2135f17fadeffeea8159a34cfdd4c30b98d8111364b913a91fd930643e9
|
||||
5e6572320437022e2746467ddf5b3561bf06e099e8e6361df27e0b2a7ed0b17b
|
||||
58fda2abf5042b35dfe04e5f8ee458a3cc26375bf309efb42c078b551a2055c7
|
||||
6d2bd30fe924d3414b64bd3920760617e6ced872364bc3bc6959a623252da002
|
||||
33d1c829be64a1e1d379caf4feec1f05a892c3ef7aa82c0be53d3c08a96c59c5
|
||||
f9f0a8a58c9e02a2b3250b88cc5c95b1e10245ca2c4161d19376580aaa90f55c
|
||||
1ef14d5ede80db78978b25ad677fd3e897a578c3af614e1fda608d40c8809707
|
||||
45e1482040e441a521953a6da2eca9bafc769e15667a07c23720d6e0cafc3ab2
|
||||
|
||||
$ sudo podman image prune -f --filter until=10h
|
||||
f3e20dc537fb04cb51672a5cb6fdf2292e61d411315549391a0d1f64e4e3097e
|
||||
324a7a3b2e0135f4226ffdd473e4099fd9e477a74230cdc35de69e84c0f9d907
|
||||
```
|
||||
|
||||
Remove all unused images from local storage with label version 1.0
|
||||
```
|
||||
$ sudo podman image prune -a -f --filter label=version=1.0
|
||||
e813d2135f17fadeffeea8159a34cfdd4c30b98d8111364b913a91fd930643e9
|
||||
5e6572320437022e2746467ddf5b3561bf06e099e8e6361df27e0b2a7ed0b17b
|
||||
58fda2abf5042b35dfe04e5f8ee458a3cc26375bf309efb42c078b551a2055c7
|
||||
6d2bd30fe924d3414b64bd3920760617e6ced872364bc3bc6959a623252da002
|
||||
33d1c829be64a1e1d379caf4feec1f05a892c3ef7aa82c0be53d3c08a96c59c5
|
||||
f9f0a8a58c9e02a2b3250b88cc5c95b1e10245ca2c4161d19376580aaa90f55c
|
||||
1ef14d5ede80db78978b25ad677fd3e897a578c3af614e1fda608d40c8809707
|
||||
45e1482040e441a521953a6da2eca9bafc769e15667a07c23720d6e0cafc3ab2
|
||||
|
||||
```
|
||||
|
||||
## SEE ALSO
|
||||
podman(1), podman-images
|
||||
|
||||
|
@ -74,6 +74,11 @@ type InfoImage struct {
|
||||
Layers []LayerInfo
|
||||
}
|
||||
|
||||
// ImageFilter is a function to determine whether a image is included
|
||||
// in command output. Images to be outputted are tested using the function.
|
||||
// A true return will include the image, a false return will exclude it.
|
||||
type ImageFilter func(*Image) bool //nolint
|
||||
|
||||
// ErrRepoTagNotFound is the error returned when the image id given doesn't match a rep tag in store
|
||||
var ErrRepoTagNotFound = stderrors.New("unable to match user input to any specific repotag")
|
||||
|
||||
|
@ -2,23 +2,78 @@ package image
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/containers/libpod/libpod/events"
|
||||
"github.com/containers/libpod/pkg/timetype"
|
||||
"github.com/containers/storage"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func generatePruneFilterFuncs(filter, filterValue string) (ImageFilter, error) {
|
||||
switch filter {
|
||||
case "label":
|
||||
var filterArray = strings.SplitN(filterValue, "=", 2)
|
||||
var filterKey = filterArray[0]
|
||||
if len(filterArray) > 1 {
|
||||
filterValue = filterArray[1]
|
||||
} else {
|
||||
filterValue = ""
|
||||
}
|
||||
return func(i *Image) bool {
|
||||
labels, err := i.Labels(context.Background())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
for labelKey, labelValue := range labels {
|
||||
if labelKey == filterKey && ("" == filterValue || labelValue == filterValue) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
|
||||
case "until":
|
||||
ts, err := timetype.GetTimestamp(filterValue, time.Now())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seconds, nanoseconds, err := timetype.ParseTimestamps(ts, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
until := time.Unix(seconds, nanoseconds)
|
||||
return func(i *Image) bool {
|
||||
if !until.IsZero() && i.Created().After((until)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}, nil
|
||||
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetPruneImages returns a slice of images that have no names/unused
|
||||
func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
|
||||
func (ir *Runtime) GetPruneImages(all bool, filterFuncs []ImageFilter) ([]*Image, error) {
|
||||
var (
|
||||
pruneImages []*Image
|
||||
)
|
||||
|
||||
allImages, err := ir.GetRWImages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, i := range allImages {
|
||||
// filter the images based on this.
|
||||
for _, filterFunc := range filterFuncs {
|
||||
if !filterFunc(i) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if len(i.Names()) == 0 {
|
||||
pruneImages = append(pruneImages, i)
|
||||
continue
|
||||
@ -38,9 +93,25 @@ func (ir *Runtime) GetPruneImages(all bool) ([]*Image, error) {
|
||||
|
||||
// PruneImages prunes dangling and optionally all unused images from the local
|
||||
// image store
|
||||
func (ir *Runtime) PruneImages(ctx context.Context, all bool) ([]string, error) {
|
||||
var prunedCids []string
|
||||
pruneImages, err := ir.GetPruneImages(all)
|
||||
func (ir *Runtime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
|
||||
var (
|
||||
prunedCids []string
|
||||
filterFuncs []ImageFilter
|
||||
)
|
||||
for _, f := range filter {
|
||||
filterSplit := strings.SplitN(f, "=", 2)
|
||||
if len(filterSplit) < 2 {
|
||||
return nil, errors.Errorf("filter input must be in the form of filter=value: %s is invalid", f)
|
||||
}
|
||||
|
||||
generatedFunc, err := generatePruneFilterFuncs(filterSplit[0], filterSplit[1])
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "invalid filter")
|
||||
}
|
||||
filterFuncs = append(filterFuncs, generatedFunc)
|
||||
}
|
||||
|
||||
pruneImages, err := ir.GetPruneImages(all, filterFuncs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "unable to get images to prune")
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/pkg/errors"
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// LocalRuntime describes a typical libpod runtime
|
||||
@ -147,8 +147,8 @@ func (r *LocalRuntime) RemoveImage(ctx context.Context, img *ContainerImage, for
|
||||
}
|
||||
|
||||
// PruneImages is wrapper into PruneImages within the image pkg
|
||||
func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
|
||||
return r.ImageRuntime().PruneImages(ctx, all)
|
||||
func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
|
||||
return r.ImageRuntime().PruneImages(ctx, all, filter)
|
||||
}
|
||||
|
||||
// Export is a wrapper to container export to a tarfile
|
||||
|
@ -415,8 +415,8 @@ func (ci *ContainerImage) History(ctx context.Context) ([]*image.History, error)
|
||||
}
|
||||
|
||||
// PruneImages is the wrapper call for a remote-client to prune images
|
||||
func (r *LocalRuntime) PruneImages(ctx context.Context, all bool) ([]string, error) {
|
||||
return iopodman.ImagesPrune().Call(r.Conn, all)
|
||||
func (r *LocalRuntime) PruneImages(ctx context.Context, all bool, filter []string) ([]string, error) {
|
||||
return iopodman.ImagesPrune().Call(r.Conn, all, filter)
|
||||
}
|
||||
|
||||
// Export is a wrapper to container export to a tarfile
|
||||
|
131
pkg/timetype/timestamp.go
Normal file
131
pkg/timetype/timestamp.go
Normal file
@ -0,0 +1,131 @@
|
||||
package timetype
|
||||
|
||||
// code adapted from https://github.com/moby/moby/blob/master/api/types/time/timestamp.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// These are additional predefined layouts for use in Time.Format and Time.Parse
|
||||
// with --since and --until parameters for `docker logs` and `docker events`
|
||||
const (
|
||||
rFC3339Local = "2006-01-02T15:04:05" // RFC3339 with local timezone
|
||||
rFC3339NanoLocal = "2006-01-02T15:04:05.999999999" // RFC3339Nano with local timezone
|
||||
dateWithZone = "2006-01-02Z07:00" // RFC3339 with time at 00:00:00
|
||||
dateLocal = "2006-01-02" // RFC3339 with local timezone and time at 00:00:00
|
||||
)
|
||||
|
||||
// GetTimestamp tries to parse given string as golang duration,
|
||||
// then RFC3339 time and finally as a Unix timestamp. If
|
||||
// any of these were successful, it returns a Unix timestamp
|
||||
// as string otherwise returns the given value back.
|
||||
// In case of duration input, the returned timestamp is computed
|
||||
// as the given reference time minus the amount of the duration.
|
||||
func GetTimestamp(value string, reference time.Time) (string, error) {
|
||||
if d, err := time.ParseDuration(value); value != "0" && err == nil {
|
||||
return strconv.FormatInt(reference.Add(-d).Unix(), 10), nil
|
||||
}
|
||||
|
||||
var format string
|
||||
// if the string has a Z or a + or three dashes use parse otherwise use parseinlocation
|
||||
parseInLocation := !(strings.ContainsAny(value, "zZ+") || strings.Count(value, "-") == 3)
|
||||
|
||||
if strings.Contains(value, ".") {
|
||||
if parseInLocation {
|
||||
format = rFC3339NanoLocal
|
||||
} else {
|
||||
format = time.RFC3339Nano
|
||||
}
|
||||
} else if strings.Contains(value, "T") {
|
||||
// we want the number of colons in the T portion of the timestamp
|
||||
tcolons := strings.Count(value, ":")
|
||||
// if parseInLocation is off and we have a +/- zone offset (not Z) then
|
||||
// there will be an extra colon in the input for the tz offset subtract that
|
||||
// colon from the tcolons count
|
||||
if !parseInLocation && !strings.ContainsAny(value, "zZ") && tcolons > 0 {
|
||||
tcolons--
|
||||
}
|
||||
if parseInLocation {
|
||||
switch tcolons {
|
||||
case 0:
|
||||
format = "2006-01-02T15"
|
||||
case 1:
|
||||
format = "2006-01-02T15:04"
|
||||
default:
|
||||
format = rFC3339Local
|
||||
}
|
||||
} else {
|
||||
switch tcolons {
|
||||
case 0:
|
||||
format = "2006-01-02T15Z07:00"
|
||||
case 1:
|
||||
format = "2006-01-02T15:04Z07:00"
|
||||
default:
|
||||
format = time.RFC3339
|
||||
}
|
||||
}
|
||||
} else if parseInLocation {
|
||||
format = dateLocal
|
||||
} else {
|
||||
format = dateWithZone
|
||||
}
|
||||
|
||||
var t time.Time
|
||||
var err error
|
||||
|
||||
if parseInLocation {
|
||||
t, err = time.ParseInLocation(format, value, time.FixedZone(reference.Zone()))
|
||||
} else {
|
||||
t, err = time.Parse(format, value)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
// if there is a `-` then it's an RFC3339 like timestamp
|
||||
if strings.Contains(value, "-") {
|
||||
return "", err // was probably an RFC3339 like timestamp but the parser failed with an error
|
||||
}
|
||||
if _, _, err := parseTimestamp(value); err != nil {
|
||||
return "", fmt.Errorf("failed to parse value as time or duration: %q", value)
|
||||
}
|
||||
return value, nil // unix timestamp in and out case (meaning: the value passed at the command line is already in the right format for passing to the server)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%d.%09d", t.Unix(), int64(t.Nanosecond())), nil
|
||||
}
|
||||
|
||||
// ParseTimestamps returns seconds and nanoseconds from a timestamp that has the
|
||||
// format "%d.%09d", time.Unix(), int64(time.Nanosecond()))
|
||||
// if the incoming nanosecond portion is longer or shorter than 9 digits it is
|
||||
// converted to nanoseconds. The expectation is that the seconds and
|
||||
// seconds will be used to create a time variable. For example:
|
||||
// seconds, nanoseconds, err := ParseTimestamp("1136073600.000000001",0)
|
||||
// if err == nil since := time.Unix(seconds, nanoseconds)
|
||||
// returns seconds as def(aultSeconds) if value == ""
|
||||
func ParseTimestamps(value string, def int64) (int64, int64, error) {
|
||||
if value == "" {
|
||||
return def, 0, nil
|
||||
}
|
||||
return parseTimestamp(value)
|
||||
}
|
||||
|
||||
func parseTimestamp(value string) (int64, int64, error) {
|
||||
sa := strings.SplitN(value, ".", 2)
|
||||
s, err := strconv.ParseInt(sa[0], 10, 64)
|
||||
if err != nil {
|
||||
return s, 0, err
|
||||
}
|
||||
if len(sa) != 2 {
|
||||
return s, 0, nil
|
||||
}
|
||||
n, err := strconv.ParseInt(sa[1], 10, 64)
|
||||
if err != nil {
|
||||
return s, n, err
|
||||
}
|
||||
// should already be in nanoseconds but just in case convert n to nanoseconds
|
||||
n = int64(float64(n) * math.Pow(float64(10), float64(9-len(sa[1]))))
|
||||
return s, n, nil
|
||||
}
|
95
pkg/timetype/timestamp_test.go
Normal file
95
pkg/timetype/timestamp_test.go
Normal file
@ -0,0 +1,95 @@
|
||||
package timetype
|
||||
|
||||
// code adapted from https://github.com/moby/moby/blob/master/api/types/time/timestamp.go
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestGetTimestamp(t *testing.T) {
|
||||
now := time.Now().In(time.UTC)
|
||||
cases := []struct {
|
||||
in, expected string
|
||||
expectedErr bool
|
||||
}{
|
||||
// Partial RFC3339 strings get parsed with second precision
|
||||
{"2006-01-02T15:04:05.999999999+07:00", "1136189045.999999999", false},
|
||||
{"2006-01-02T15:04:05.999999999Z", "1136214245.999999999", false},
|
||||
{"2006-01-02T15:04:05.999999999", "1136214245.999999999", false},
|
||||
{"2006-01-02T15:04:05Z", "1136214245.000000000", false},
|
||||
{"2006-01-02T15:04:05", "1136214245.000000000", false},
|
||||
{"2006-01-02T15:04:0Z", "", true},
|
||||
{"2006-01-02T15:04:0", "", true},
|
||||
{"2006-01-02T15:04Z", "1136214240.000000000", false},
|
||||
{"2006-01-02T15:04+00:00", "1136214240.000000000", false},
|
||||
{"2006-01-02T15:04-00:00", "1136214240.000000000", false},
|
||||
{"2006-01-02T15:04", "1136214240.000000000", false},
|
||||
{"2006-01-02T15:0Z", "", true},
|
||||
{"2006-01-02T15:0", "", true},
|
||||
{"2006-01-02T15Z", "1136214000.000000000", false},
|
||||
{"2006-01-02T15+00:00", "1136214000.000000000", false},
|
||||
{"2006-01-02T15-00:00", "1136214000.000000000", false},
|
||||
{"2006-01-02T15", "1136214000.000000000", false},
|
||||
{"2006-01-02T1Z", "1136163600.000000000", false},
|
||||
{"2006-01-02T1", "1136163600.000000000", false},
|
||||
{"2006-01-02TZ", "", true},
|
||||
{"2006-01-02T", "", true},
|
||||
{"2006-01-02+00:00", "1136160000.000000000", false},
|
||||
{"2006-01-02-00:00", "1136160000.000000000", false},
|
||||
{"2006-01-02-00:01", "1136160060.000000000", false},
|
||||
{"2006-01-02Z", "1136160000.000000000", false},
|
||||
{"2006-01-02", "1136160000.000000000", false},
|
||||
{"2015-05-13T20:39:09Z", "1431549549.000000000", false},
|
||||
|
||||
// unix timestamps returned as is
|
||||
{"1136073600", "1136073600", false},
|
||||
{"1136073600.000000001", "1136073600.000000001", false},
|
||||
// Durations
|
||||
{"1m", fmt.Sprintf("%d", now.Add(-1*time.Minute).Unix()), false},
|
||||
{"1.5h", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
|
||||
{"1h30m", fmt.Sprintf("%d", now.Add(-90*time.Minute).Unix()), false},
|
||||
|
||||
{"invalid", "", true},
|
||||
{"", "", true},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
o, err := GetTimestamp(c.in, now)
|
||||
if o != c.expected ||
|
||||
(err == nil && c.expectedErr) ||
|
||||
(err != nil && !c.expectedErr) {
|
||||
t.Errorf("wrong value for '%s'. expected:'%s' got:'%s' with error: `%s`", c.in, c.expected, o, err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseTimestamps(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
def, expectedS, expectedN int64
|
||||
expectedErr bool
|
||||
}{
|
||||
// unix timestamps
|
||||
{"1136073600", 0, 1136073600, 0, false},
|
||||
{"1136073600.000000001", 0, 1136073600, 1, false},
|
||||
{"1136073600.0000000010", 0, 1136073600, 1, false},
|
||||
{"1136073600.00000001", 0, 1136073600, 10, false},
|
||||
{"foo.bar", 0, 0, 0, true},
|
||||
{"1136073600.bar", 0, 1136073600, 0, true},
|
||||
{"", -1, -1, 0, false},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
s, n, err := ParseTimestamps(c.in, c.def)
|
||||
if s != c.expectedS ||
|
||||
n != c.expectedN ||
|
||||
(err == nil && c.expectedErr) ||
|
||||
(err != nil && !c.expectedErr) {
|
||||
t.Errorf("wrong values for input `%s` with default `%d` expected:'%d'seconds and `%d`nanosecond got:'%d'seconds and `%d`nanoseconds with error: `%s`", c.in, c.def, c.expectedS, c.expectedN, s, n, err)
|
||||
t.Fail()
|
||||
}
|
||||
}
|
||||
}
|
@ -21,7 +21,7 @@ import (
|
||||
"github.com/containers/image/v5/transports/alltransports"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/libpod/cmd/podman/shared"
|
||||
"github.com/containers/libpod/cmd/podman/varlink"
|
||||
iopodman "github.com/containers/libpod/cmd/podman/varlink"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
@ -29,7 +29,7 @@ import (
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/containers/libpod/utils"
|
||||
"github.com/containers/storage/pkg/archive"
|
||||
"github.com/opencontainers/image-spec/specs-go/v1"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/opencontainers/runtime-spec/specs-go"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -740,8 +740,8 @@ func (i *LibpodAPI) ContainerRunlabel(call iopodman.VarlinkCall, input iopodman.
|
||||
}
|
||||
|
||||
// ImagesPrune ....
|
||||
func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool) error {
|
||||
prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all)
|
||||
func (i *LibpodAPI) ImagesPrune(call iopodman.VarlinkCall, all bool, filter []string) error {
|
||||
prunedImages, err := i.Runtime.ImageRuntime().PruneImages(context.TODO(), all, []string{})
|
||||
if err != nil {
|
||||
return call.ReplyErrorOccurred(err.Error())
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ var _ = Describe("Podman prune", func() {
|
||||
hasNone, _ := none.GrepString("<none>")
|
||||
Expect(hasNone).To(BeTrue())
|
||||
|
||||
prune := podmanTest.Podman([]string{"image", "prune"})
|
||||
prune := podmanTest.Podman([]string{"image", "prune", "-f"})
|
||||
prune.WaitWithDefaultTimeout()
|
||||
Expect(prune.ExitCode()).To(Equal(0))
|
||||
|
||||
@ -78,7 +78,7 @@ var _ = Describe("Podman prune", func() {
|
||||
|
||||
It("podman image prune unused images", func() {
|
||||
podmanTest.RestoreAllArtifacts()
|
||||
prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-a"})
|
||||
prune := podmanTest.PodmanNoCache([]string{"image", "prune", "-af"})
|
||||
prune.WaitWithDefaultTimeout()
|
||||
Expect(prune.ExitCode()).To(Equal(0))
|
||||
|
||||
|
Reference in New Issue
Block a user