wait: add --ignore option

In the recent past, I met the frequent need to wait for a container to
exist that, at the same time, may get removed (e.g., system tests in [1]).

Add an `--ignore` option to podman-wait which will ignore errors when a
specified container is missing and mark its exit code as -1.  Also
remove ID fields from the WaitReport.  It is actually not used by
callers and removing it makes the code simpler and faster.

Once merged, we can go over the tests and simplify them.

[1] github.com/containers/podman/pull/16852

Signed-off-by: Valentin Rothberg <vrothberg@redhat.com>
This commit is contained in:
Valentin Rothberg
2022-12-15 14:24:33 +01:00
parent 18f1a8046b
commit f4d0496b54
6 changed files with 50 additions and 16 deletions

View File

@ -53,6 +53,8 @@ func waitFlags(cmd *cobra.Command) {
flags.StringVarP(&waitInterval, intervalFlagName, "i", "250ms", "Time Interval to wait before polling for completion") flags.StringVarP(&waitInterval, intervalFlagName, "i", "250ms", "Time Interval to wait before polling for completion")
_ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone) _ = cmd.RegisterFlagCompletionFunc(intervalFlagName, completion.AutocompleteNone)
flags.BoolVarP(&waitOptions.Ignore, "ignore", "", false, "Ignore if a container does not exist")
conditionFlagName := "condition" conditionFlagName := "condition"
flags.StringSliceVar(&waitConditions, conditionFlagName, []string{}, "Condition to wait on") flags.StringSliceVar(&waitConditions, conditionFlagName, []string{}, "Condition to wait on")
_ = cmd.RegisterFlagCompletionFunc(conditionFlagName, common.AutocompleteWaitCondition) _ = cmd.RegisterFlagCompletionFunc(conditionFlagName, common.AutocompleteWaitCondition)

View File

@ -23,6 +23,10 @@ Condition to wait on (default "stopped")
Print usage statement Print usage statement
#### **--ignore**
Ignore errors when a specified container is missing and mark its return code as -1.
#### **--interval**, **-i**=*duration* #### **--interval**, **-i**=*duration*
Time interval to wait before polling for completion. A duration string is a sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Time unit defaults to "ms". Time interval to wait before polling for completion. A duration string is a sequence of decimal numbers, each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". Time unit defaults to "ms".
@ -46,6 +50,9 @@ $ podman wait 860a4b23
$ podman wait mywebserver myftpserver $ podman wait mywebserver myftpserver
0 0
125 125
$ podman wait --ignore does-not-exist
-1
``` ```
## SEE ALSO ## SEE ALSO

View File

@ -52,11 +52,11 @@ type ContainerRunlabelReport struct{}
type WaitOptions struct { type WaitOptions struct {
Condition []define.ContainerStatus Condition []define.ContainerStatus
Interval time.Duration Interval time.Duration
Ignore bool
Latest bool Latest bool
} }
type WaitReport struct { type WaitReport struct {
Id string //nolint:revive,stylecheck
Error error Error error
ExitCode int32 ExitCode int32
} }

View File

@ -139,13 +139,29 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string,
} }
func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) { func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, options entities.WaitOptions) ([]entities.WaitReport, error) {
ctrs, err := getContainersByContext(false, options.Latest, false, namesOrIds, ic.Libpod) responses := make([]entities.WaitReport, 0, len(namesOrIds))
if err != nil { if options.Latest {
return nil, err ctr, err := ic.Libpod.GetLatestContainer()
if err != nil {
if options.Ignore && errors.Is(err, define.ErrNoSuchCtr) {
responses = append(responses, entities.WaitReport{ExitCode: -1})
return responses, nil
}
return nil, err
}
namesOrIds = append(namesOrIds, ctr.ID())
} }
responses := make([]entities.WaitReport, 0, len(ctrs)) for _, n := range namesOrIds {
for _, c := range ctrs { c, err := ic.Libpod.LookupContainer(n)
response := entities.WaitReport{Id: c.ID()} if err != nil {
if options.Ignore && errors.Is(err, define.ErrNoSuchCtr) {
responses = append(responses, entities.WaitReport{ExitCode: -1})
continue
}
return nil, err
}
response := entities.WaitReport{}
if options.Condition == nil { if options.Condition == nil {
options.Condition = []define.ContainerStatus{define.ContainerStateStopped, define.ContainerStateExited} options.Condition = []define.ContainerStatus{define.ContainerStateStopped, define.ContainerStateExited}
} }

View File

@ -38,17 +38,17 @@ func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrID string,
} }
func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, opts entities.WaitOptions) ([]entities.WaitReport, error) { func (ic *ContainerEngine) ContainerWait(ctx context.Context, namesOrIds []string, opts entities.WaitOptions) ([]entities.WaitReport, error) {
cons, err := getContainersByContext(ic.ClientCtx, false, false, namesOrIds) responses := make([]entities.WaitReport, 0, len(namesOrIds))
if err != nil {
return nil, err
}
responses := make([]entities.WaitReport, 0, len(cons))
options := new(containers.WaitOptions).WithCondition(opts.Condition).WithInterval(opts.Interval.String()) options := new(containers.WaitOptions).WithCondition(opts.Condition).WithInterval(opts.Interval.String())
for _, c := range cons { for _, n := range namesOrIds {
response := entities.WaitReport{Id: c.ID} response := entities.WaitReport{}
exitCode, err := containers.Wait(ic.ClientCtx, c.ID, options) exitCode, err := containers.Wait(ic.ClientCtx, n, options)
if err != nil { if err != nil {
response.Error = err if opts.Ignore && errorhandling.Contains(err, define.ErrNoSuchCtr) {
response.ExitCode = -1
} else {
response.Error = err
}
} else { } else {
response.ExitCode = exitCode response.ExitCode = exitCode
} }

View File

@ -20,6 +20,15 @@ load helpers
run_podman rm $rand run_podman rm $rand
is "$output" "$rand" "display raw input" is "$output" "$rand" "display raw input"
run_podman 125 inspect $rand run_podman 125 inspect $rand
run_podman 125 wait $rand
run_podman wait --ignore $rand
is "$output" "-1" "wait --ignore will mark missing containers with -1"
if !is_remote; then
# remote does not support the --latest flag
run_podman wait --ignore --latest
is "$output" "-1" "wait --ignore will mark missing containers with -1"
fi
} }
@test "podman rm - running container, w/o and w/ force" { @test "podman rm - running container, w/o and w/ force" {