From 175e4e50480f7b9efc3c852c20605687cbb7655a Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Thu, 30 Oct 2025 11:36:29 +0100 Subject: [PATCH] client: Client.TaskLogs: close reader on context cancellation Use a cancelReadCloser to automatically close the reader when the context is cancelled. Consumers are still recommended to manually close the reader, but the cancelReadCloser makes the Close idempotent. Signed-off-by: Sebastiaan van Stijn --- client/task_logs.go | 28 ++++++------------- .../github.com/moby/moby/client/task_logs.go | 28 ++++++------------- 2 files changed, 18 insertions(+), 38 deletions(-) diff --git a/client/task_logs.go b/client/task_logs.go index a4278b1f06..e4de019f30 100644 --- a/client/task_logs.go +++ b/client/task_logs.go @@ -27,9 +27,14 @@ type TaskLogsResult interface { io.ReadCloser } -// TaskLogs returns the logs generated by a task. -// It's up to the caller to close the stream. +// TaskLogs returns the logs generated by a service in a [TaskLogsResult]. +// as an [io.ReadCloser]. Callers should close the stream. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error) { + // TODO(thaJeztah): this function needs documentation about the format of ths stream (similar to for container logs) + // TODO(thaJeztah): migrate CLI utilities to the client where suitable; https://github.com/docker/cli/blob/v29.0.0-rc.1/cli/command/service/logs.go#L73-L348 + query := url.Values{} if options.ShowStdout { query.Set("stdout", "1") @@ -65,30 +70,15 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogs return nil, err } return &taskLogsResult{ - body: resp.Body, + ReadCloser: newCancelReadCloser(ctx, resp.Body), }, nil } type taskLogsResult struct { - // body must be closed to avoid a resource leak - body io.ReadCloser + io.ReadCloser } var ( _ io.ReadCloser = (*taskLogsResult)(nil) _ ContainerLogsResult = (*taskLogsResult)(nil) ) - -func (r *taskLogsResult) Read(p []byte) (int, error) { - if r == nil || r.body == nil { - return 0, io.EOF - } - return r.body.Read(p) -} - -func (r *taskLogsResult) Close() error { - if r == nil || r.body == nil { - return nil - } - return r.body.Close() -} diff --git a/vendor/github.com/moby/moby/client/task_logs.go b/vendor/github.com/moby/moby/client/task_logs.go index a4278b1f06..e4de019f30 100644 --- a/vendor/github.com/moby/moby/client/task_logs.go +++ b/vendor/github.com/moby/moby/client/task_logs.go @@ -27,9 +27,14 @@ type TaskLogsResult interface { io.ReadCloser } -// TaskLogs returns the logs generated by a task. -// It's up to the caller to close the stream. +// TaskLogs returns the logs generated by a service in a [TaskLogsResult]. +// as an [io.ReadCloser]. Callers should close the stream. +// +// The underlying [io.ReadCloser] is automatically closed if the context is canceled, func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogsOptions) (TaskLogsResult, error) { + // TODO(thaJeztah): this function needs documentation about the format of ths stream (similar to for container logs) + // TODO(thaJeztah): migrate CLI utilities to the client where suitable; https://github.com/docker/cli/blob/v29.0.0-rc.1/cli/command/service/logs.go#L73-L348 + query := url.Values{} if options.ShowStdout { query.Set("stdout", "1") @@ -65,30 +70,15 @@ func (cli *Client) TaskLogs(ctx context.Context, taskID string, options TaskLogs return nil, err } return &taskLogsResult{ - body: resp.Body, + ReadCloser: newCancelReadCloser(ctx, resp.Body), }, nil } type taskLogsResult struct { - // body must be closed to avoid a resource leak - body io.ReadCloser + io.ReadCloser } var ( _ io.ReadCloser = (*taskLogsResult)(nil) _ ContainerLogsResult = (*taskLogsResult)(nil) ) - -func (r *taskLogsResult) Read(p []byte) (int, error) { - if r == nil || r.body == nil { - return 0, io.EOF - } - return r.body.Read(p) -} - -func (r *taskLogsResult) Close() error { - if r == nil || r.body == nil { - return nil - } - return r.body.Close() -}