From ed30ae4a8aacf87cb9be3cfed2e43499cb4d8649 Mon Sep 17 00:00:00 2001
From: Jakub Guzik <jakubmguzik@gmail.com>
Date: Mon, 9 Aug 2021 23:57:26 +0200
Subject: Add until filter to podman pod ps

This commit adds additional until filter to podman pod ps (ls/list).
Additionally, it also adds descriptions for podman pod ps filters available
via http api.

Signed-off-by: Jakub Guzik <jakubmguzik@gmail.com>
---
 docs/source/markdown/podman-pod-ps.1.md |  1 +
 pkg/api/server/register_pods.go         | 13 ++++++++++++-
 pkg/domain/filters/pods.go              | 11 +++++++++++
 test/apiv2/40-pods.at                   |  3 +++
 test/e2e/pod_ps_test.go                 | 16 ++++++++++++++++
 5 files changed, 43 insertions(+), 1 deletion(-)

diff --git a/docs/source/markdown/podman-pod-ps.1.md b/docs/source/markdown/podman-pod-ps.1.md
index 156adccaa..ed0789e93 100644
--- a/docs/source/markdown/podman-pod-ps.1.md
+++ b/docs/source/markdown/podman-pod-ps.1.md
@@ -98,6 +98,7 @@ Valid filters are listed below:
 | id         | [ID] Pod's ID (accepts regex)                                                         |
 | name       | [Name] Pod's name (accepts regex)                                                     |
 | label      | [Key] or [Key=Value] Label assigned to a container                                    |
+| until      | Only list pods created before given timestamp                                         |
 | status     | Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded` |
 | network    | [Network] name or full ID of network                                                  |
 | ctr-names  | Container name within the pod (accepts regex)                                         |
diff --git a/pkg/api/server/register_pods.go b/pkg/api/server/register_pods.go
index 3bcc50ba4..58234005e 100644
--- a/pkg/api/server/register_pods.go
+++ b/pkg/api/server/register_pods.go
@@ -17,7 +17,18 @@ func (s *APIServer) registerPodsHandlers(r *mux.Router) error {
 	// - in: query
 	//   name: filters
 	//   type: string
-	//   description: needs description and plumbing for filters
+	//   description: |
+	//      JSON encoded value of the filters (a map[string][]string) to process on the pods list. Available filters:
+	//        - `id=<pod-id>` Matches all of pod id.
+	//        - `label=<key>` or `label=<key>:<value>` Matches pods based on the presence of a label alone or a label and a value.
+	//        - `name=<pod-name>` Matches all of pod name.
+	//        - `until=<timestamp>` List pods created before this timestamp. The `<timestamp>` can be Unix timestamps, date formatted timestamps, or Go duration strings (e.g. `10m`, `1h30m`) computed relative to the daemon machine’s time.
+	//        - `status=<pod-status>` Pod's status: `stopped`, `running`, `paused`, `exited`, `dead`, `created`, `degraded`.
+	//        - `network=<pod-network>` Name or full ID of network.
+	//        - `ctr-names=<pod-ctr-names>` Container name within the pod.
+	//        - `ctr-ids=<pod-ctr-ids>` Container ID within the pod.
+	//        - `ctr-status=<pod-ctr-status>` Container status within the pod.
+	//        - `ctr-number=<pod-ctr-number>` Number of containers in the pod.
 	// responses:
 	//   200:
 	//     $ref: "#/responses/ListPodsResponse"
diff --git a/pkg/domain/filters/pods.go b/pkg/domain/filters/pods.go
index 9a1c7d19d..9a2f0a3ba 100644
--- a/pkg/domain/filters/pods.go
+++ b/pkg/domain/filters/pods.go
@@ -116,6 +116,17 @@ func GeneratePodFilterFunc(filter string, filterValues []string) (
 			labels := p.Labels()
 			return util.MatchLabelFilters(filterValues, labels)
 		}, nil
+	case "until":
+		return func(p *libpod.Pod) bool {
+			until, err := util.ComputeUntilTimestamp(filterValues)
+			if err != nil {
+				return false
+			}
+			if p.CreatedTime().Before(until) {
+				return true
+			}
+			return false
+		}, nil
 	case "network":
 		return func(p *libpod.Pod) bool {
 			infra, err := p.InfraContainer()
diff --git a/test/apiv2/40-pods.at b/test/apiv2/40-pods.at
index 94c72dbaa..985b26411 100644
--- a/test/apiv2/40-pods.at
+++ b/test/apiv2/40-pods.at
@@ -19,6 +19,9 @@ t GET  libpod/pods/json            200 \
   .[0].Id=$pod_id \
   .[0].Containers\|length=1
 
+t GET libpod/pods/json?filters='{"until":["500000"]}' 200 length=0
+t GET libpod/pods/json?filters='{"until":["5000000000"]}' 200 length=1
+
 # Cannot create a dup pod with the same name
 t POST "libpod/pods/create (dup pod)" name=foo 409 \
   .cause="pod already exists"
diff --git a/test/e2e/pod_ps_test.go b/test/e2e/pod_ps_test.go
index c27539d6f..b4a0df904 100644
--- a/test/e2e/pod_ps_test.go
+++ b/test/e2e/pod_ps_test.go
@@ -108,6 +108,22 @@ var _ = Describe("Podman ps", func() {
 		Expect(result).Should(Exit(0))
 	})
 
+	It("podman pod ps --filter until", func() {
+		name := "mypod"
+		_, ec, _ := podmanTest.CreatePod(map[string][]string{"--name": {name}})
+		Expect(ec).To(Equal(0))
+
+		result := podmanTest.Podman([]string{"pod", "ps", "--filter", "until=50"})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(result.OutputToString()).To(Not(ContainSubstring(name)))
+
+		result = podmanTest.Podman([]string{"pod", "ps", "--filter", "until=5000000000"})
+		result.WaitWithDefaultTimeout()
+		Expect(result).Should(Exit(0))
+		Expect(result.OutputToString()).To(ContainSubstring(name))
+	})
+
 	It("podman pod ps filter name regexp", func() {
 		_, ec, podid := podmanTest.CreatePod(map[string][]string{"--name": {"mypod"}})
 		Expect(ec).To(Equal(0))
-- 
cgit v1.2.3-54-g00ecf