mirror of
https://github.com/podman-container-tools/podman.git
synced 2026-08-05 00:15:44 +00:00
Merge pull request #28641 from jiwahn/feat/container-annotation-filter
Feat/container annotation filter
This commit is contained in:
commit
5b98ee331f
8 changed files with 96 additions and 17 deletions
|
|
@ -1791,11 +1791,12 @@ func AutocompletePsFilters(cmd *cobra.Command, _ []string, toComplete string) ([
|
|||
define.HealthCheckUnhealthy,
|
||||
}, cobra.ShellCompDirectiveNoFileComp
|
||||
},
|
||||
"id=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeIDs) },
|
||||
"label=": nil,
|
||||
"name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) },
|
||||
"network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeDefault) },
|
||||
"pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) },
|
||||
"id=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeIDs) },
|
||||
"annotation=": nil,
|
||||
"label=": nil,
|
||||
"name=": func(s string) ([]string, cobra.ShellCompDirective) { return getContainers(cmd, s, completeNames) },
|
||||
"network=": func(s string) ([]string, cobra.ShellCompDirective) { return getNetworks(cmd, s, completeDefault) },
|
||||
"pod=": func(s string) ([]string, cobra.ShellCompDirective) { return getPods(cmd, s, completeDefault) },
|
||||
"restart-policy=": func(_ string) ([]string, cobra.ShellCompDirective) {
|
||||
return []string{
|
||||
define.RestartPolicyAlways,
|
||||
|
|
@ -1875,11 +1876,14 @@ func AutocompleteImageFilters(cmd *cobra.Command, _ []string, toComplete string)
|
|||
}
|
||||
|
||||
// AutocompletePruneFilters - Autocomplete container/image prune --filter options.
|
||||
func AutocompletePruneFilters(_ *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
func AutocompletePruneFilters(cmd *cobra.Command, _ []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
kv := keyValueCompletion{
|
||||
"label=": nil,
|
||||
"until=": nil,
|
||||
}
|
||||
if cmd.CommandPath() == "podman container prune" {
|
||||
kv["annotation="] = nil
|
||||
}
|
||||
return completeKeyValues(toComplete, kv)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ Valid filters are listed below:
|
|||
|----------------------|-------------------------------------------------------------------------------------------------|
|
||||
| id | [ID] Container's ID (CID prefix match by default; accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| annotation | [Key] or [Key=Value] Annotation assigned to a container |
|
||||
| annotation! | [Key] or [Key=Value] Annotation NOT assigned to a container |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| label! | [Key] or [Key=Value] Label NOT assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
|
|
|
|||
|
|
@ -18,12 +18,13 @@ If there is more than one filter, the `--filter` option should be passed multipl
|
|||
|
||||
Supported filters:
|
||||
|
||||
| Filter | Description |
|
||||
|:------:|------------------------------------------------------------------------------------------------------|
|
||||
| label | Only remove containers, with (or without, in the case of label!=[...] is used) the specified labels. |
|
||||
| until | Only remove containers created before given timestamp. |
|
||||
| Filter | Description |
|
||||
|:-----------:|----------------------------------------------------------------------------------------------------------------|
|
||||
| annotation | Only remove containers, with (or without, in the case of annotation!=[...] is used) the specified annotations. |
|
||||
| label | Only remove containers, with (or without, in the case of label!=[...] is used) the specified labels. |
|
||||
| until | Only remove containers created before given timestamp. |
|
||||
|
||||
The `label` *filter* accepts two formats. One is the `label`=*key* or `label`=*key*=*value*, which removes containers with the specified labels. The other format is the `label!`=*key* or `label!`=*key*=*value*, which removes containers without the specified labels.
|
||||
The `label` *filter* accepts two formats. One is the `label`=*key* or `label`=*key*=*value*, which removes containers with the specified labels. The other format is the `label!`=*key* or `label!`=*key*=*value*, which removes containers without the specified labels. The `annotation` and `annotation!` filters work the same way for container annotations.
|
||||
|
||||
**NOTE:** `label!` filters are combined with **AND**, so that the behavior is consistent with `label`, while in Docker, they are combined with **OR**.
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ Note: Podman shares containers storage with other tools such as Buildah and CRI-
|
|||
|
||||
Display external containers that are not controlled by Podman but are stored in containers storage. These external containers are generally created via other container technology such as Buildah or CRI-O and may depend on the same container images that Podman is also using. External containers are denoted with either a 'buildah' or 'storage' in the COMMAND and STATUS column of the ps output.
|
||||
|
||||
The **restart-policy**, **volume**, **health**, and **annotation** filters are not applicable for external containers.
|
||||
|
||||
@@option filter.container
|
||||
|
||||
#### **--format**=*format*
|
||||
|
|
@ -249,6 +251,14 @@ $ podman ps --filter label=app=frontend
|
|||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
ff660efda598 docker.io/library/nginx:latest nginx -g daemon o... 3 minutes ago Up 3 minutes 0.0.0.0:8080->80/tcp webserver
|
||||
```
|
||||
|
||||
Filter containers by annotation.
|
||||
```
|
||||
$ podman ps --filter annotation=io.podman.annotations.autoremove=false
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
5e3694604817 quay.io/centos/centos:latest sleep 300 3 minutes ago Up 3 minutes centos-test
|
||||
```
|
||||
|
||||
Filter containers by volume.
|
||||
```
|
||||
$ podman ps --filter volume=mydata
|
||||
|
|
|
|||
|
|
@ -75,9 +75,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||
// description: |
|
||||
// A JSON encoded value of the filters (a `map[string][]string`) to process on the containers list. Available filters:
|
||||
// - `ancestor`=(`<image-name>[:<tag>]`, `<image id>`, or `<image@digest>`)
|
||||
// - `annotation`=(`key` or `"key=value"`) of a container annotation
|
||||
// - `before`=(`<container id>` or `<container name>`)
|
||||
// - `expose`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`)
|
||||
// - `exited=<int>` containers with exit code of `<int>`
|
||||
// - `expose`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`)
|
||||
// - `health`=(`starting`, `healthy`, `unhealthy` or `none`)
|
||||
// - `id=<ID>` a container's ID
|
||||
// - `is-task`=(`true` or `false`)
|
||||
|
|
@ -112,8 +113,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||
// type: string
|
||||
// description: |
|
||||
// Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters:
|
||||
// - `until=<timestamp>` Prune containers 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.
|
||||
// - `annotation` (`annotation=<key>`, `annotation=<key>=<value>`, `annotation!=<key>`, or `annotation!=<key>=<value>`) Prune containers with (or without, in case `annotation!=...` is used) the specified annotations.
|
||||
// - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune containers with (or without, in case `label!=...` is used) the specified labels.
|
||||
// - `until=<timestamp>` Prune containers 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.
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
|
|
@ -815,9 +817,10 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||
// description: |
|
||||
// A JSON encoded value of the filters (a `map[string][]string`) to process on the containers list. Available filters:
|
||||
// - `ancestor`=(`<image-name>[:<tag>]`, `<image id>`, or `<image@digest>`)
|
||||
// - `annotation`=(`key` or `"key=value"`) of a container annotation
|
||||
// - `before`=(`<container id>` or `<container name>`)
|
||||
// - `expose`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`)
|
||||
// - `exited=<int>` containers with exit code of `<int>`
|
||||
// - `expose`=(`<port>[/<proto>]` or `<startport-endport>/[<proto>]`)
|
||||
// - `health`=(`starting`, `healthy`, `unhealthy` or `none`)
|
||||
// - `id=<ID>` a container's ID
|
||||
// - `is-task`=(`true` or `false`)
|
||||
|
|
@ -851,8 +854,9 @@ func (s *APIServer) registerContainersHandlers(r *mux.Router) error {
|
|||
// type: string
|
||||
// description: |
|
||||
// Filters to process on the prune list, encoded as JSON (a `map[string][]string`). Available filters:
|
||||
// - `until=<timestamp>` Prune containers 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.
|
||||
// - `annotation` (`annotation=<key>`, `annotation=<key>=<value>`, `annotation!=<key>`, or `annotation!=<key>=<value>`) Prune containers with (or without, in case `annotation!=...` is used) the specified annotations.
|
||||
// - `label` (`label=<key>`, `label=<key>=<value>`, `label!=<key>`, or `label!=<key>=<value>`) Prune containers with (or without, in case `label!=...` is used) the specified labels.
|
||||
// - `until=<timestamp>` Prune containers 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.
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
|
|
|
|||
|
|
@ -34,6 +34,14 @@ func GenerateContainerFilterFuncs(filter string, filterValues []string, r *libpo
|
|||
return func(c *libpod.Container) bool {
|
||||
return filters.MatchNegatedLabelFilters(filterValues, c.Labels())
|
||||
}, nil
|
||||
case "annotation":
|
||||
return func(c *libpod.Container) bool {
|
||||
return filters.MatchLabelFilters(filterValues, c.ConfigNoCopy().Spec.Annotations)
|
||||
}, nil
|
||||
case "annotation!":
|
||||
return func(c *libpod.Container) bool {
|
||||
return filters.MatchNegatedLabelFilters(filterValues, c.ConfigNoCopy().Spec.Annotations)
|
||||
}, nil
|
||||
case "name":
|
||||
// we only have to match one name
|
||||
return func(c *libpod.Container) bool {
|
||||
|
|
@ -311,6 +319,14 @@ func GeneratePruneContainerFilterFuncs(filter string, filterValues []string, _ *
|
|||
return func(c *libpod.Container) bool {
|
||||
return filters.MatchNegatedLabelFilters(filterValues, c.Labels())
|
||||
}, nil
|
||||
case "annotation":
|
||||
return func(c *libpod.Container) bool {
|
||||
return filters.MatchLabelFilters(filterValues, c.ConfigNoCopy().Spec.Annotations)
|
||||
}, nil
|
||||
case "annotation!":
|
||||
return func(c *libpod.Container) bool {
|
||||
return filters.MatchNegatedLabelFilters(filterValues, c.ConfigNoCopy().Spec.Annotations)
|
||||
}, nil
|
||||
case "until":
|
||||
return prepareUntilFilterFunc(filterValues)
|
||||
}
|
||||
|
|
@ -330,7 +346,7 @@ func prepareUntilFilterFunc(filterValues []string) (func(container *libpod.Conta
|
|||
}, nil
|
||||
}
|
||||
|
||||
// GenerateContainerFilterFuncs return ContainerFilter functions based of filter.
|
||||
// GenerateExternalContainerFilterFuncs return ContainerFilter functions for external containers
|
||||
func GenerateExternalContainerFilterFuncs(filter string, filterValues []string, r *libpod.Runtime) (func(listContainer *types.ListContainer) bool, error) {
|
||||
switch filter {
|
||||
case "id":
|
||||
|
|
@ -534,7 +550,7 @@ func GenerateExternalContainerFilterFuncs(filter string, filterValues []string,
|
|||
}
|
||||
return false
|
||||
}, nil
|
||||
case "restart-policy", "volume", "health":
|
||||
case "restart-policy", "volume", "health", "annotation", "annotation!":
|
||||
return nil, fmt.Errorf("filter %s is not applicable for external containers", filter)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,22 @@ var _ = Describe("Podman prune", func() {
|
|||
Expect(podmanTest.NumberOfContainers()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman container prune filters by annotation", func() {
|
||||
podmanTest.PodmanExitCleanly("create", "--annotation", "prune=me", "--name", "prune-me", BB)
|
||||
podmanTest.PodmanExitCleanly("create", "--annotation", "prune=keep", "--name", "keep-me", BB)
|
||||
podmanTest.PodmanExitCleanly("container", "prune", "-f", "--filter", "annotation=prune=me")
|
||||
session := podmanTest.PodmanExitCleanly("ps", "-a", "--format", "{{.Names}}")
|
||||
Expect(session.OutputToStringArray()).To(Equal([]string{"keep-me"}))
|
||||
})
|
||||
|
||||
It("podman container prune filters by negated annotation", func() {
|
||||
podmanTest.PodmanExitCleanly("create", "--annotation", "prune=me", "--name", "prune-me", BB)
|
||||
podmanTest.PodmanExitCleanly("create", "--annotation", "prune=keep", "--name", "keep-me", BB)
|
||||
podmanTest.PodmanExitCleanly("container", "prune", "-f", "--filter", "annotation!=prune=me")
|
||||
session := podmanTest.PodmanExitCleanly("ps", "-a", "--format", "{{.Names}}")
|
||||
Expect(session.OutputToStringArray()).To(Equal([]string{"prune-me"}))
|
||||
})
|
||||
|
||||
It("podman image prune - remove only dangling images", func() {
|
||||
session := podmanTest.Podman([]string{"images", "-a"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
|
|
|
|||
|
|
@ -211,6 +211,20 @@ var _ = Describe("Podman ps", func() {
|
|||
Expect(actual).ToNot(ContainSubstring("table"))
|
||||
})
|
||||
|
||||
It("podman ps --filter annotation=test=with,comma", func() {
|
||||
ctrAlpha := "first"
|
||||
podmanTest.PodmanExitCleanly("create", "--annotation", "test=with,comma", "--name", ctrAlpha, ALPINE, "top")
|
||||
|
||||
ctrBravo := "second"
|
||||
podmanTest.PodmanExitCleanly("create", "--name", ctrBravo, ALPINE, "top")
|
||||
|
||||
result := podmanTest.PodmanExitCleanly("ps", "-a", "--format", "{{.Names}}", "--filter", "annotation=test=with,comma")
|
||||
Expect(result.OutputToStringArray()).To(Equal([]string{ctrAlpha}))
|
||||
|
||||
result = podmanTest.PodmanExitCleanly("ps", "-a", "--format", "{{.Names}}", "--filter", "annotation!=test=with,comma")
|
||||
Expect(result.OutputToStringArray()).To(Equal([]string{ctrBravo}))
|
||||
})
|
||||
|
||||
It("podman ps namespace flag", func() {
|
||||
_, ec, _ := podmanTest.RunLsContainer("")
|
||||
Expect(ec).To(Equal(0))
|
||||
|
|
@ -1014,6 +1028,18 @@ var _ = Describe("Podman ps", func() {
|
|||
Expect(output).To(HaveLen(1))
|
||||
})
|
||||
|
||||
It("podman ps rejects annotation filters for external containers", func() {
|
||||
podmanTest.PodmanExitCleanly("create", "--name", "test", BB)
|
||||
|
||||
session := podmanTest.Podman([]string{"ps", "-a", "--external", "--filter", "annotation=test=value"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitWithError(125, "filter annotation is not applicable for external containers"))
|
||||
|
||||
session = podmanTest.Podman([]string{"ps", "-a", "--external", "--filter", "annotation!=test=value"})
|
||||
session.WaitWithDefaultTimeout()
|
||||
Expect(session).Should(ExitWithError(125, "filter annotation! is not applicable for external containers"))
|
||||
})
|
||||
|
||||
// This test checks ps filtering of external container created earlier than a given
|
||||
It("podman ps filter external by container created earlier than a given", func() {
|
||||
early := podmanTest.Podman([]string{"create", "--name", "early", BB})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue