Repository: moby/moby
File: client/container_wait.go (Go)

Code before:
package client

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/url"

	"github.com/moby/moby/api/types/container"
)

const containerWaitErrorMsgLimit = 2 * 1024 /* Max: 2KiB */

// ContainerWait waits until the specified container is in a certain state
// indicated by the given condition, either "not-running" ([container.WaitConditionNotRunning])
// (default),  "next-exit" ([container.WaitConditionNextExit]), or "removed".
// ([container.WaitConditionRemoved]).
//
// If this client's API version is before 1.30, "condition" is ignored and
// ContainerWait returns immediately with the two channels, as the server
// waits as if the condition were "not-running".
//
// If this client's API version is at least 1.30, ContainerWait blocks until
// the request has been acknowledged by the server (with a response header),
// then returns two channels on which the caller can wait for the exit status
// of the container or an error if there was a problem either beginning the
// wait request or in getting the response. This allows the caller to
// synchronize ContainerWait with other calls, such as specifying a
// "next-exit" condition ([container.WaitConditionNextExit]) before
// issuing a [Client.ContainerStart] request.
func (cli *Client) ContainerWait(ctx context.Context, containerID string, condition container.WaitCondition) (<-chan container.WaitResponse, <-chan error) {
	resultC := make(chan container.WaitResponse)
	errC := make(chan error, 1)

	containerID, err := trimID("container", containerID)
	if err != nil {
		errC <- err
		return resultC, errC
	}

	query := url.Values{}
	if condition != "" {
		query.Set("condition", string(condition))
	}

	resp, err := cli.post(ctx, "/containers/"+containerID+"/wait", query, nil, nil)
	if err != nil {
		defer ensureReaderClosed(resp)

Review comment (refactor):
I think this always returns a single result (it blocks until it's reaching the desired state).


But I may have a look at follow-ups; wondering if we should

- add an explicit `Timeout` option in the options; this would set `context.WithTimeout` under the hood.
- abstract away the channel, and just make the function blocking
- but we may need to have some options for an `Ack()` method for situations where the wait must be registered before starting the container 🤔 



Code after:
package client

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"io"
	"net/url"

	"github.com/moby/moby/api/types/container"
)

const containerWaitErrorMsgLimit = 2 * 1024 /* Max: 2KiB */

// ContainerWaitOptions holds options for container wait operations.
type ContainerWaitOptions struct {
	Condition container.WaitCondition
}

// ContainerWaitResult defines the result of a container wait operation.
type ContainerWaitResult struct {
	Results <-chan container.WaitResponse
	Errors  <-chan error
}

// ContainerWait waits until the specified container is in a certain state
// indicated by the given condition, either "not-running" ([container.WaitConditionNotRunning])
// (default),  "next-exit" ([container.WaitConditionNextExit]), or "removed".
// ([container.WaitConditionRemoved]).
//
// If this client's API version is before 1.30, "condition" is ignored and
// ContainerWait returns immediately with the two channels, as the server
// waits as if the condition were "not-running".
//
// If this client's API version is at least 1.30, ContainerWait blocks until
// the request has been acknowledged by the server (with a response header),
// then returns two channels on which the caller can wait for the exit status
// of the container or an error if there was a problem either beginning the
// wait request or in getting the response. This allows the caller to
// synchronize ContainerWait with other calls, such as specifying a
// "next-exit" condition ([container.WaitConditionNextExit]) before
// issuing a [Client.ContainerStart] request.
func (cli *Client) ContainerWait(ctx context.Context, containerID string, options ContainerWaitOptions) ContainerWaitResult {
	resultC := make(chan container.WaitResponse)
	errC := make(chan error, 1)

	containerID, err := trimID("container", containerID)
	if err != nil {
		errC <- err