Add since as valid filter option for volume subcommands

Adds support for `since` as a valid filter option for `podman volume ls`
and `podman volume prune`.

Implements: #19228
Initially suggested from: #19119

Signed-off-by: Jake Correnti <jakecorrenti+github@proton.me>
This commit is contained in:
Jake Correnti
2023-07-18 14:49:27 -04:00
parent f8eaec6f84
commit e55e128fcd
8 changed files with 98 additions and 18 deletions

View File

@ -23,15 +23,16 @@ which is exclusive. Filters with different keys always work exclusive.
Volumes can be filtered by the following attributes:
| **Filter** | **Description** |
| ---------- | ------------------------------------------------------------------------------------- |
| dangling | [Dangling] Matches all volumes not referenced by any containers |
| driver | [Driver] Matches volumes based on their driver |
| label | [Key] or [Key=Value] Label assigned to a volume |
| name | [Name] Volume name (accepts regex) |
| opt | Matches a storage driver options |
| scope | Filters volume by scope |
| until | Only remove volumes created before given timestamp |
| **Filter** | **Description** |
| ---------- | ------------------------------------------------------------------------------------- |
| dangling | [Dangling] Matches all volumes not referenced by any containers |
| driver | [Driver] Matches volumes based on their driver |
| label | [Key] or [Key=Value] Label assigned to a volume |
| name | [Name] Volume name (accepts regex) |
| opt | Matches a storage driver options |
| scope | Filters volume by scope |
| after/since | Filter by volumes created after the given VOLUME (name or tag) |
| until | Only remove volumes created before given timestamp |
#### **--format**=*format*

View File

@ -26,7 +26,8 @@ Supported filters:
| Filter | Description |
| :----------------: | --------------------------------------------------------------------------- |
| *label* | Only remove volumes, with (or without, in the case of label!=[...] is used) the specified labels. |
| *until* | Only remove volumes created before given timestamp. |
| *until* | Only remove volumes created before given timestamp. |
| *after/since* | Filter by volumes created after the given VOLUME (name or tag) |
The `label` *filter* accepts two formats. One is the `label`=*key* or `label`=*key*=*value*, which removes volumes with the specified labels. The other format is the `label!`=*key* or `label!`=*key*=*value*, which removes volumes without the specified labels.

View File

@ -44,7 +44,7 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
volumeFilters := []libpod.VolumeFilter{}
for filter, filterValues := range *filtersMap {
filterFunc, err := filters.GenerateVolumeFilters(filter, filterValues)
filterFunc, err := filters.GenerateVolumeFilters(filter, filterValues, runtime)
if err != nil {
utils.InternalServerError(w, err)
}
@ -276,7 +276,7 @@ func PruneVolumes(w http.ResponseWriter, r *http.Request) {
f := (url.Values)(*filterMap)
filterFuncs := []libpod.VolumeFilter{}
for filter, filterValues := range f {
filterFunc, err := filters.GeneratePruneVolumeFilters(filter, filterValues)
filterFunc, err := filters.GeneratePruneVolumeFilters(filter, filterValues, runtime)
if err != nil {
utils.Error(w, http.StatusInternalServerError, fmt.Errorf("failed to parse filters for %s: %w", f.Encode(), err))
return

View File

@ -121,7 +121,7 @@ func ListVolumes(w http.ResponseWriter, r *http.Request) {
volumeFilters := []libpod.VolumeFilter{}
for filter, filterValues := range *filterMap {
filterFunc, err := filters.GenerateVolumeFilters(filter, filterValues)
filterFunc, err := filters.GenerateVolumeFilters(filter, filterValues, runtime)
if err != nil {
utils.InternalServerError(w, err)
return
@ -168,7 +168,7 @@ func pruneVolumesHelper(r *http.Request) ([]*reports.PruneReport, error) {
f := (url.Values)(*filterMap)
filterFuncs := []libpod.VolumeFilter{}
for filter, filterValues := range f {
filterFunc, err := filters.GeneratePruneVolumeFilters(filter, filterValues)
filterFunc, err := filters.GeneratePruneVolumeFilters(filter, filterValues, runtime)
if err != nil {
return nil, err
}

View File

@ -3,14 +3,17 @@ package filters
import (
"fmt"
"strings"
"time"
"github.com/containers/common/pkg/filters"
"github.com/containers/podman/v4/libpod"
"github.com/containers/podman/v4/pkg/util"
)
func GenerateVolumeFilters(filter string, filterValues []string) (libpod.VolumeFilter, error) {
func GenerateVolumeFilters(filter string, filterValues []string, runtime *libpod.Runtime) (libpod.VolumeFilter, error) {
switch filter {
case "after", "since":
return createAfterFilterVolumeFunction(filterValues, runtime)
case "name":
return func(v *libpod.Volume) bool {
return util.StringMatchRegexSlice(v.Name(), filterValues)
@ -98,8 +101,10 @@ func GenerateVolumeFilters(filter string, filterValues []string) (libpod.VolumeF
return nil, fmt.Errorf("%q is an invalid volume filter", filter)
}
func GeneratePruneVolumeFilters(filter string, filterValues []string) (libpod.VolumeFilter, error) {
func GeneratePruneVolumeFilters(filter string, filterValues []string, runtime *libpod.Runtime) (libpod.VolumeFilter, error) {
switch filter {
case "after", "since":
return createAfterFilterVolumeFunction(filterValues, runtime)
case "label":
return func(v *libpod.Volume) bool {
return filters.MatchLabelFilters(filterValues, v.Labels())
@ -126,3 +131,19 @@ func createUntilFilterVolumeFunction(filterValues []string) (libpod.VolumeFilter
return false
}, nil
}
func createAfterFilterVolumeFunction(filterValues []string, runtime *libpod.Runtime) (libpod.VolumeFilter, error) {
var createTime time.Time
for _, filterValue := range filterValues {
vol, err := runtime.LookupVolume(filterValue)
if err != nil {
return nil, err
}
if createTime.IsZero() || createTime.After(vol.CreatedTime()) {
createTime = vol.CreatedTime()
}
}
return func(v *libpod.Volume) bool {
return createTime.Before(v.CreatedTime())
}, nil
}

View File

@ -124,7 +124,7 @@ func (ic *ContainerEngine) VolumeInspect(ctx context.Context, namesOrIds []strin
func (ic *ContainerEngine) VolumePrune(ctx context.Context, options entities.VolumePruneOptions) ([]*reports.PruneReport, error) {
funcs := []libpod.VolumeFilter{}
for filter, filterValues := range options.Filters {
filterFunc, err := filters.GenerateVolumeFilters(filter, filterValues)
filterFunc, err := filters.GenerateVolumeFilters(filter, filterValues, ic.Libpod)
if err != nil {
return nil, err
}
@ -144,7 +144,7 @@ func (ic *ContainerEngine) pruneVolumesHelper(ctx context.Context, filterFuncs [
func (ic *ContainerEngine) VolumeList(ctx context.Context, opts entities.VolumeListOptions) ([]*entities.VolumeListReport, error) {
volumeFilters := []libpod.VolumeFilter{}
for filter, value := range opts.Filter {
filterFunc, err := filters.GenerateVolumeFilters(filter, value)
filterFunc, err := filters.GenerateVolumeFilters(filter, value, ic.Libpod)
if err != nil {
return nil, err
}

View File

@ -211,4 +211,34 @@ var _ = Describe("Podman volume ls", func() {
Expect(session.OutputToStringArray()).To(HaveLen(1))
Expect(session.OutputToStringArray()[0]).To(Equal(vol3Name))
})
It("podman ls volume with --filter since/after", func() {
vol1 := "vol1"
vol2 := "vol2"
vol3 := "vol3"
session := podmanTest.Podman([]string{"volume", "create", vol1})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "create", vol2})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "create", vol3})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "ls", "-q", "--filter", "since=" + vol1})
session.WaitWithDefaultTimeout()
Expect(session.OutputToStringArray()).To(HaveLen(2))
Expect(session.OutputToStringArray()[0]).To(Equal(vol2))
Expect(session.OutputToStringArray()[1]).To(Equal(vol3))
session = podmanTest.Podman([]string{"volume", "ls", "-q", "--filter", "after=" + vol1})
session.WaitWithDefaultTimeout()
Expect(session.OutputToStringArray()).To(HaveLen(2))
Expect(session.OutputToStringArray()[0]).To(Equal(vol2))
Expect(session.OutputToStringArray()[1]).To(Equal(vol3))
})
})

View File

@ -163,4 +163,31 @@ var _ = Describe("Podman volume prune", func() {
Expect(session).Should(Exit(0))
Expect(session.OutputToStringArray()).To(BeEmpty())
})
It("podman volume prune --filter since/after", func() {
vol1 := "vol1"
vol2 := "vol2"
vol3 := "vol3"
session := podmanTest.Podman([]string{"volume", "create", vol1})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "create", vol2})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "create", vol3})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "prune", "-f", "--filter", "since=" + vol1})
session.WaitWithDefaultTimeout()
Expect(session).To(Exit(0))
session = podmanTest.Podman([]string{"volume", "ls", "-q"})
session.WaitWithDefaultTimeout()
Expect(session.OutputToStringArray()).To(HaveLen(1))
Expect(session.OutputToStringArray()[0]).To(Equal(vol1))
})
})