aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOpenShift Merge Robot <openshift-merge-robot@users.noreply.github.com>2019-11-22 21:56:12 +0100
committerGitHub <noreply@github.com>2019-11-22 21:56:12 +0100
commitef240f4cd0fd3bf45f79522eb0cc3aad228e32ac (patch)
tree10bdcd842996eba5a981ff0be296f270bee40346
parent35605c02fd9a83f09c61323942243e1a9cf1d4f1 (diff)
parentc7d911e77633a0990a79d05ec3fdc1e04b0fbde1 (diff)
downloadpodman-ef240f4cd0fd3bf45f79522eb0cc3aad228e32ac.tar.gz
podman-ef240f4cd0fd3bf45f79522eb0cc3aad228e32ac.tar.bz2
podman-ef240f4cd0fd3bf45f79522eb0cc3aad228e32ac.zip
Merge pull request #4512 from kunalkushwaha/prune-filter
image prune command fixed as per docker image prune.
-rwxr-xr-xAPI.md4
-rw-r--r--cmd/podman/cliconfig/config.go4
-rw-r--r--cmd/podman/images_prune.go20
-rw-r--r--cmd/podman/system_prune.go3
-rw-r--r--cmd/podman/varlink/io.podman.varlink2
-rw-r--r--docs/source/markdown/podman-image-prune.1.md41
-rw-r--r--libpod/image/image.go5
-rw-r--r--libpod/image/prune.go79
-rw-r--r--pkg/adapter/runtime.go6
-rw-r--r--pkg/adapter/runtime_remote.go4
-rw-r--r--pkg/timetype/timestamp.go131
-rw-r--r--pkg/timetype/timestamp_test.go95
-rw-r--r--pkg/varlinkapi/images.go8
-rw-r--r--test/e2e/prune_test.go4
14 files changed, 383 insertions, 23 deletions
diff --git a/API.md b/API.md
index d96ea6cd0..c288e6b28 100755
--- a/API.md
+++ b/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
diff --git a/cmd/podman/cliconfig/config.go b/cmd/podman/cliconfig/config.go
index 541b2e05d..745f49651 100644
--- a/cmd/podman/cliconfig/config.go
+++ b/cmd/podman/cliconfig/config.go
@@ -175,7 +175,9 @@ type HistoryValues struct {
}
type PruneImagesValues struct {
PodmanCommand
- All bool
+ All bool
+ Force bool
+ Filter []string
}
type PruneContainersValues struct {
diff --git a/cmd/podman/images_prune.go b/cmd/podman/images_prune.go
index 5745edd6b..2b498f83d 100644
--- a/cmd/podman/images_prune.go
+++ b/cmd/podman/images_prune.go
@@ -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)
diff --git a/cmd/podman/system_prune.go b/cmd/podman/system_prune.go
index b499d8dd2..c4d76b2dd 100644
--- a/cmd/podman/system_prune.go
+++ b/cmd/podman/system_prune.go
@@ -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 {
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index f9339fccb..4f810dd53 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -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)
diff --git a/docs/source/markdown/podman-image-prune.1.md b/docs/source/markdown/podman-image-prune.1.md
index b844a9f63..0155ebcd1 100644
--- a/docs/source/markdown/podman-image-prune.1.md
+++ b/docs/source/markdown/podman-image-prune.1.md
@@ -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
diff --git a/libpod/image/image.go b/libpod/image/image.go
index 75ac85311..fa75be44d 100644
--- a/libpod/image/image.go
+++ b/libpod/image/image.go
@@ -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")
diff --git a/libpod/image/prune.go b/libpod/image/prune.go
index 006cbdf22..f5be8ed50 100644
--- a/libpod/image/prune.go
+++ b/libpod/image/prune.go
@@ -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")
}
diff --git a/pkg/adapter/runtime.go b/pkg/adapter/runtime.go
index 81a43853c..069283bde 100644
--- a/pkg/adapter/runtime.go
+++ b/pkg/adapter/runtime.go
@@ -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
diff --git a/pkg/adapter/runtime_remote.go b/pkg/adapter/runtime_remote.go
index 01bc8e454..ddd4b5271 100644
--- a/pkg/adapter/runtime_remote.go
+++ b/pkg/adapter/runtime_remote.go
@@ -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
diff --git a/pkg/timetype/timestamp.go b/pkg/timetype/timestamp.go
new file mode 100644
index 000000000..eb904a574
--- /dev/null
+++ b/pkg/timetype/timestamp.go
@@ -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
+}
diff --git a/pkg/timetype/timestamp_test.go b/pkg/timetype/timestamp_test.go
new file mode 100644
index 000000000..0fffb85a9
--- /dev/null
+++ b/pkg/timetype/timestamp_test.go
@@ -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()
+ }
+ }
+}
diff --git a/pkg/varlinkapi/images.go b/pkg/varlinkapi/images.go
index 8d44e6373..c27088805 100644
--- a/pkg/varlinkapi/images.go
+++ b/pkg/varlinkapi/images.go
@@ -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())
}
diff --git a/test/e2e/prune_test.go b/test/e2e/prune_test.go
index df0525a79..eba12dca4 100644
--- a/test/e2e/prune_test.go
+++ b/test/e2e/prune_test.go
@@ -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))