mirror of
https://github.com/containers/podman.git
synced 2025-06-17 15:08:08 +08:00
Podman stop --filter flag
Filter flag is added for podman stop and podman --remote stop. Filtering logic is implemented in getContainersAndInputByContext(). Start filtering can be manipulated to use this logic as well to limit redundancy. Signed-off-by: Karthik Elango <kelango@redhat.com>
This commit is contained in:

committed by
Matthew Heon

parent
a78be890ee
commit
6d84a9952f
@ -49,7 +49,9 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
stopOptions = entities.StopOptions{}
|
||||
stopOptions = entities.StopOptions{
|
||||
Filters: make(map[string][]string),
|
||||
}
|
||||
stopTimeout uint
|
||||
)
|
||||
|
||||
@ -67,6 +69,10 @@ func stopFlags(cmd *cobra.Command) {
|
||||
flags.UintVarP(&stopTimeout, timeFlagName, "t", containerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container")
|
||||
_ = cmd.RegisterFlagCompletionFunc(timeFlagName, completion.AutocompleteNone)
|
||||
|
||||
filterFlagName := "filter"
|
||||
flags.StringSliceVarP(&filters, filterFlagName, "f", []string{}, "Filter output based on conditions given")
|
||||
_ = cmd.RegisterFlagCompletionFunc(filterFlagName, common.AutocompletePsFilters)
|
||||
|
||||
if registry.IsRemote() {
|
||||
_ = flags.MarkHidden("cidfile")
|
||||
_ = flags.MarkHidden("ignore")
|
||||
@ -97,7 +103,6 @@ func stop(cmd *cobra.Command, args []string) error {
|
||||
if cmd.Flag("time").Changed {
|
||||
stopOptions.Timeout = &stopTimeout
|
||||
}
|
||||
|
||||
for _, cidFile := range cidFiles {
|
||||
content, err := ioutil.ReadFile(cidFile)
|
||||
if err != nil {
|
||||
@ -107,6 +112,14 @@ func stop(cmd *cobra.Command, args []string) error {
|
||||
args = append(args, id)
|
||||
}
|
||||
|
||||
for _, f := range filters {
|
||||
split := strings.SplitN(f, "=", 2)
|
||||
if len(split) < 2 {
|
||||
return fmt.Errorf("invalid filter %q", f)
|
||||
}
|
||||
stopOptions.Filters[split[0]] = append(stopOptions.Filters[split[0]], split[1])
|
||||
}
|
||||
|
||||
responses, err := registry.ContainerEngine().ContainerStop(context.Background(), args, stopOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -86,6 +86,13 @@ func CheckAllLatestAndIDFile(c *cobra.Command, args []string, ignoreArgLen bool,
|
||||
specifiedIDFile = true
|
||||
}
|
||||
|
||||
if c.Flags().Changed("filter") {
|
||||
if argLen > 0 {
|
||||
return errors.New("--filter takes no arguments")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if specifiedIDFile && (specifiedAll || specifiedLatest) {
|
||||
return fmt.Errorf("--all, --latest, and --%s cannot be used together", idFileFlag)
|
||||
} else if specifiedAll && specifiedLatest {
|
||||
|
@ -25,6 +25,30 @@ Stop all running containers. This does not include paused containers.
|
||||
|
||||
Read container ID from the specified file and remove the container. Can be specified multiple times.
|
||||
|
||||
#### **--filter**, **-f**=*filter*
|
||||
|
||||
Filter what containers are going to be stopped.
|
||||
Multiple filters can be given with multiple uses of the --filter flag.
|
||||
Filters with the same key work inclusive with the only exception being
|
||||
`label` which is exclusive. Filters with different keys always work exclusive.
|
||||
|
||||
Valid filters are listed below:
|
||||
|
||||
| **Filter** | **Description** |
|
||||
| --------------- | -------------------------------------------------------------------------------- |
|
||||
| id | [ID] Container's ID (accepts regex) |
|
||||
| name | [Name] Container's name (accepts regex) |
|
||||
| label | [Key] or [Key=Value] Label assigned to a container |
|
||||
| exited | [Int] Container's exit code |
|
||||
| status | [Status] Container's status: 'created', 'exited', 'paused', 'running', 'unknown' |
|
||||
| ancestor | [ImageName] Image or descendant used to create container |
|
||||
| before | [ID] or [Name] Containers created before this container |
|
||||
| since | [ID] or [Name] Containers created since this container |
|
||||
| volume | [VolumeName] or [MountpointDestination] Volume mounted in container |
|
||||
| health | [Status] healthy or unhealthy |
|
||||
| pod | [Pod] name or full or partial ID of pod |
|
||||
| network | [Network] name or full ID of network |
|
||||
|
||||
#### **--ignore**, **-i**
|
||||
|
||||
Ignore errors when specified containers are not in the container store. A user
|
||||
|
@ -33,9 +33,7 @@ func StopContainer(w http.ResponseWriter, r *http.Request) {
|
||||
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
|
||||
return
|
||||
}
|
||||
|
||||
name := utils.GetName(r)
|
||||
|
||||
options := entities.StopOptions{
|
||||
Ignore: query.Ignore,
|
||||
}
|
||||
|
@ -80,6 +80,7 @@ type PauseUnpauseReport struct {
|
||||
}
|
||||
|
||||
type StopOptions struct {
|
||||
Filters map[string][]string
|
||||
All bool
|
||||
Ignore bool
|
||||
Latest bool
|
||||
|
@ -37,12 +37,29 @@ import (
|
||||
)
|
||||
|
||||
// getContainersAndInputByContext gets containers whether all, latest, or a slice of names/ids
|
||||
// is specified. It also returns a list of the corresponding input name used to look up each container.
|
||||
func getContainersAndInputByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) {
|
||||
// is specified. It also returns a list of the corresponding input name used to lookup each container.
|
||||
func getContainersAndInputByContext(all, latest bool, names []string, filters map[string][]string, runtime *libpod.Runtime) (ctrs []*libpod.Container, rawInput []string, err error) {
|
||||
var ctr *libpod.Container
|
||||
ctrs = []*libpod.Container{}
|
||||
filterFuncs := make([]libpod.ContainerFilter, 0, len(filters))
|
||||
|
||||
switch {
|
||||
case len(filters) > 0:
|
||||
for k, v := range filters {
|
||||
generatedFunc, err := dfilters.GenerateContainerFilterFuncs(k, v, runtime)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
filterFuncs = append(filterFuncs, generatedFunc)
|
||||
}
|
||||
ctrs, err = runtime.GetContainers(filterFuncs...)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rawInput = []string{}
|
||||
for _, candidate := range ctrs {
|
||||
rawInput = append(rawInput, candidate.ID())
|
||||
}
|
||||
case all:
|
||||
ctrs, err = runtime.GetAllContainers()
|
||||
case latest:
|
||||
@ -66,13 +83,13 @@ func getContainersAndInputByContext(all, latest bool, names []string, runtime *l
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
return ctrs, rawInput, err
|
||||
}
|
||||
|
||||
// getContainersByContext gets containers whether all, latest, or a slice of names/ids
|
||||
// is specified.
|
||||
func getContainersByContext(all, latest bool, names []string, runtime *libpod.Runtime) (ctrs []*libpod.Container, err error) {
|
||||
ctrs, _, err = getContainersAndInputByContext(all, latest, names, runtime)
|
||||
ctrs, _, err = getContainersAndInputByContext(all, latest, names, nil, runtime)
|
||||
return
|
||||
}
|
||||
|
||||
@ -150,7 +167,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
|
||||
}
|
||||
func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, options entities.StopOptions) ([]*entities.StopReport, error) {
|
||||
names := namesOrIds
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, names, ic.Libpod)
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, names, options.Filters, ic.Libpod)
|
||||
if err != nil && !(options.Ignore && errors.Is(err, define.ErrNoSuchCtr)) {
|
||||
return nil, err
|
||||
}
|
||||
@ -228,7 +245,7 @@ func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []strin
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, namesOrIds, ic.Libpod)
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(options.All, options.Latest, namesOrIds, nil, ic.Libpod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -874,7 +891,7 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri
|
||||
}
|
||||
}
|
||||
}
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(all, options.Latest, containersNamesOrIds, ic.Libpod)
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(all, options.Latest, containersNamesOrIds, options.Filters, ic.Libpod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -91,8 +91,7 @@ func (ic *ContainerEngine) ContainerUnpause(ctx context.Context, namesOrIds []st
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []string, opts entities.StopOptions) ([]*entities.StopReport, error) {
|
||||
reports := []*entities.StopReport{}
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds)
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, opts.Ignore, namesOrIds, opts.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -104,6 +103,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
|
||||
if to := opts.Timeout; to != nil {
|
||||
options.WithTimeout(*to)
|
||||
}
|
||||
reports := []*entities.StopReport{}
|
||||
for _, c := range ctrs {
|
||||
report := entities.StopReport{
|
||||
Id: c.ID,
|
||||
@ -134,7 +134,7 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, opts entities.KillOptions) ([]*entities.KillReport, error) {
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, false, namesOrIds)
|
||||
ctrs, rawInputs, err := getContainersAndInputByContext(ic.ClientCtx, opts.All, false, namesOrIds, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -15,25 +15,29 @@ import (
|
||||
// FIXME: the `ignore` parameter is very likely wrong here as it should rather
|
||||
// be used on *errors* from operations such as remove.
|
||||
func getContainersByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string) ([]entities.ListContainer, error) {
|
||||
ctrs, _, err := getContainersAndInputByContext(contextWithConnection, all, ignore, namesOrIDs)
|
||||
ctrs, _, err := getContainersAndInputByContext(contextWithConnection, all, ignore, namesOrIDs, nil)
|
||||
return ctrs, err
|
||||
}
|
||||
|
||||
func getContainersAndInputByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string) ([]entities.ListContainer, []string, error) {
|
||||
func getContainersAndInputByContext(contextWithConnection context.Context, all, ignore bool, namesOrIDs []string, filters map[string][]string) ([]entities.ListContainer, []string, error) {
|
||||
if all && len(namesOrIDs) > 0 {
|
||||
return nil, nil, errors.New("cannot look up containers and all")
|
||||
}
|
||||
options := new(containers.ListOptions).WithAll(true).WithSync(true)
|
||||
options := new(containers.ListOptions).WithAll(true).WithSync(true).WithFilters(filters)
|
||||
allContainers, err := containers.List(contextWithConnection, options)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rawInputs := []string{}
|
||||
if all {
|
||||
switch {
|
||||
case len(filters) > 0:
|
||||
for i := range allContainers {
|
||||
namesOrIDs = append(namesOrIDs, allContainers[i].ID)
|
||||
}
|
||||
case all:
|
||||
for i := range allContainers {
|
||||
rawInputs = append(rawInputs, allContainers[i].ID)
|
||||
}
|
||||
|
||||
return allContainers, rawInputs, err
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
@ -363,4 +364,45 @@ var _ = Describe("Podman stop", func() {
|
||||
Expect(session).Should(Exit(0))
|
||||
Expect(podmanTest.NumberOfContainersRunning()).To(Equal(0))
|
||||
})
|
||||
|
||||
It("podman stop --filter", func() {
|
||||
session1 := podmanTest.Podman([]string{"container", "create", ALPINE})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
cid1 := session1.OutputToString()
|
||||
|
||||
session1 = podmanTest.Podman([]string{"container", "create", ALPINE})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
cid2 := session1.OutputToString()
|
||||
|
||||
session1 = podmanTest.Podman([]string{"container", "create", ALPINE})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
cid3 := session1.OutputToString()
|
||||
shortCid3 := cid3[0:5]
|
||||
|
||||
session1 = podmanTest.Podman([]string{"start", "--all"})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
|
||||
session1 = podmanTest.Podman([]string{"stop", cid1, "-f", "status=running"})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(125))
|
||||
|
||||
session1 = podmanTest.Podman([]string{"stop", "-a", "--filter", fmt.Sprintf("id=%swrongid", shortCid3)})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
Expect(session1.OutputToString()).To(HaveLen(0))
|
||||
|
||||
session1 = podmanTest.Podman([]string{"stop", "-a", "--filter", fmt.Sprintf("id=%s", shortCid3)})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
Expect(session1.OutputToString()).To(BeEquivalentTo(cid3))
|
||||
|
||||
session1 = podmanTest.Podman([]string{"stop", "-f", fmt.Sprintf("id=%s", cid2)})
|
||||
session1.WaitWithDefaultTimeout()
|
||||
Expect(session1).Should(Exit(0))
|
||||
Expect(session1.OutputToString()).To(BeEquivalentTo(cid2))
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user