From 66e38ca55d9a0079eb7d317b5a70c2623fc90d20 Mon Sep 17 00:00:00 2001 From: Matej Vasek Date: Fri, 7 May 2021 22:35:20 +0200 Subject: fix: improved "containers/{name}/wait" endpoint Using event API to detect changes to container instead of polling. Polling was unreliable, sometime change of a state might have been missed. Signed-off-by: Matej Vasek --- pkg/api/handlers/utils/containers.go | 35 ++++++++++++++++++++++++++++------- test/apiv2/26-containersWait.at | 2 +- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/pkg/api/handlers/utils/containers.go b/pkg/api/handlers/utils/containers.go index c4c9cc2ea..6c708f74e 100644 --- a/pkg/api/handlers/utils/containers.go +++ b/pkg/api/handlers/utils/containers.go @@ -7,6 +7,7 @@ import ( "strconv" "time" + "github.com/containers/podman/v3/libpod/events" "github.com/containers/podman/v3/pkg/domain/entities" "github.com/containers/podman/v3/pkg/domain/infra/abi" @@ -175,7 +176,7 @@ func waitDockerCondition(ctx context.Context, containerName string, interval tim var code int32 switch dockerCondition { case "next-exit": - code, err = waitNextExit(containerWait) + code, err = waitNextExit(ctx, containerName) case "removed": code, err = waitRemoved(containerWait) case "not-running", "": @@ -202,12 +203,32 @@ func waitRemoved(ctrWait containerWaitFn) (int32, error) { return code, err } -func waitNextExit(ctrWait containerWaitFn) (int32, error) { - _, err := ctrWait(define.ContainerStateRunning) - if err != nil { - return -1, err - } - return ctrWait(notRunningStates...) +func waitNextExit(ctx context.Context, containerName string) (int32, error) { + runtime := ctx.Value("runtime").(*libpod.Runtime) + containerEngine := &abi.ContainerEngine{Libpod: runtime} + eventChannel := make(chan *events.Event) + errChannel := make(chan error) + opts := entities.EventsOptions{ + EventChan: eventChannel, + Filter: []string{"event=died", fmt.Sprintf("container=%s", containerName)}, + Stream: true, + } + + // ctx is used to cancel event watching goroutine + ctx, cancel := context.WithCancel(ctx) + defer cancel() + go func() { + errChannel <- containerEngine.Events(ctx, opts) + }() + + evt, ok := <-eventChannel + if ok { + return int32(evt.ContainerExitCode), nil + } + // if ok == false then containerEngine.Events() has exited + // it may happen if request was canceled (e.g. client closed connection prematurely) or + // the server is in process of shutting down + return -1, <-errChannel } func waitNotRunning(ctrWait containerWaitFn) (int32, error) { diff --git a/test/apiv2/26-containersWait.at b/test/apiv2/26-containersWait.at index 6a628e55a..ec16c35df 100644 --- a/test/apiv2/26-containersWait.at +++ b/test/apiv2/26-containersWait.at @@ -15,7 +15,7 @@ CTR="WaitTestingCtr" t POST "containers/nonExistent/wait?condition=next-exit" 404 -podman create --name "${CTR}" --entrypoint '["sleep", "0.5"]' "${IMAGE}" +podman create --name "${CTR}" --entrypoint '["true"]' "${IMAGE}" t POST "containers/${CTR}/wait?condition=non-existent-cond" 400 -- cgit v1.2.3-54-g00ecf