From 7dc9d39ca1d6cc7bc3d7879a21718a65ed4c8bcb Mon Sep 17 00:00:00 2001 From: Sebastiaan van Stijn Date: Mon, 3 Nov 2025 18:12:15 +0100 Subject: [PATCH] client: make ContainerLogsResult an interface Signed-off-by: Sebastiaan van Stijn --- client/container_logs.go | 48 +++++++++---------- .../moby/moby/client/container_logs.go | 48 +++++++++---------- 2 files changed, 46 insertions(+), 50 deletions(-) diff --git a/client/container_logs.go b/client/container_logs.go index 71c698e9eb..2245fa9149 100644 --- a/client/container_logs.go +++ b/client/container_logs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/url" - "sync" "time" "github.com/moby/moby/client/internal/timestamp" @@ -24,9 +23,8 @@ type ContainerLogsOptions struct { } // ContainerLogsResult is the result of a container logs operation. -type ContainerLogsResult struct { - rc io.ReadCloser - close func() error +type ContainerLogsResult interface { + io.ReadCloser } // ContainerLogs returns the logs generated by a container in an [io.ReadCloser]. @@ -58,7 +56,7 @@ type ContainerLogsResult struct { func (cli *Client) ContainerLogs(ctx context.Context, containerID string, options ContainerLogsOptions) (ContainerLogsResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return ContainerLogsResult{}, err + return nil, err } query := url.Values{} @@ -73,7 +71,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option if options.Since != "" { ts, err := timestamp.GetTimestamp(options.Since, time.Now()) if err != nil { - return ContainerLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err) + return nil, fmt.Errorf(`invalid value for "since": %w`, err) } query.Set("since", ts) } @@ -81,7 +79,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option if options.Until != "" { ts, err := timestamp.GetTimestamp(options.Until, time.Now()) if err != nil { - return ContainerLogsResult{}, fmt.Errorf(`invalid value for "until": %w`, err) + return nil, fmt.Errorf(`invalid value for "until": %w`, err) } query.Set("until", ts) } @@ -101,33 +99,33 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option resp, err := cli.get(ctx, "/containers/"+containerID+"/logs", query, nil) if err != nil { - return ContainerLogsResult{}, err + return nil, err } - return newContainerLogsResult(resp.Body), nil + return &containerLogsResult{ + body: resp.Body, + }, nil } -func newContainerLogsResult(rc io.ReadCloser) ContainerLogsResult { - if rc == nil { - panic("rc cannot be nil") - } - return ContainerLogsResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } +type containerLogsResult struct { + // body must be closed to avoid a resource leak + body io.ReadCloser } -// Read implements the io.Reader interface. -func (r ContainerLogsResult) Read(p []byte) (n int, err error) { - if r.rc == nil { +var ( + _ io.ReadCloser = (*containerLogsResult)(nil) + _ ContainerLogsResult = (*containerLogsResult)(nil) +) + +func (r *containerLogsResult) Read(p []byte) (int, error) { + if r == nil || r.body == nil { return 0, io.EOF } - return r.rc.Read(p) + return r.body.Read(p) } -// Close closes the underlying reader. -func (r ContainerLogsResult) Close() error { - if r.close == nil { +func (r *containerLogsResult) Close() error { + if r == nil || r.body == nil { return nil } - return r.close() + return r.body.Close() } diff --git a/vendor/github.com/moby/moby/client/container_logs.go b/vendor/github.com/moby/moby/client/container_logs.go index 71c698e9eb..2245fa9149 100644 --- a/vendor/github.com/moby/moby/client/container_logs.go +++ b/vendor/github.com/moby/moby/client/container_logs.go @@ -5,7 +5,6 @@ import ( "fmt" "io" "net/url" - "sync" "time" "github.com/moby/moby/client/internal/timestamp" @@ -24,9 +23,8 @@ type ContainerLogsOptions struct { } // ContainerLogsResult is the result of a container logs operation. -type ContainerLogsResult struct { - rc io.ReadCloser - close func() error +type ContainerLogsResult interface { + io.ReadCloser } // ContainerLogs returns the logs generated by a container in an [io.ReadCloser]. @@ -58,7 +56,7 @@ type ContainerLogsResult struct { func (cli *Client) ContainerLogs(ctx context.Context, containerID string, options ContainerLogsOptions) (ContainerLogsResult, error) { containerID, err := trimID("container", containerID) if err != nil { - return ContainerLogsResult{}, err + return nil, err } query := url.Values{} @@ -73,7 +71,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option if options.Since != "" { ts, err := timestamp.GetTimestamp(options.Since, time.Now()) if err != nil { - return ContainerLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err) + return nil, fmt.Errorf(`invalid value for "since": %w`, err) } query.Set("since", ts) } @@ -81,7 +79,7 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option if options.Until != "" { ts, err := timestamp.GetTimestamp(options.Until, time.Now()) if err != nil { - return ContainerLogsResult{}, fmt.Errorf(`invalid value for "until": %w`, err) + return nil, fmt.Errorf(`invalid value for "until": %w`, err) } query.Set("until", ts) } @@ -101,33 +99,33 @@ func (cli *Client) ContainerLogs(ctx context.Context, containerID string, option resp, err := cli.get(ctx, "/containers/"+containerID+"/logs", query, nil) if err != nil { - return ContainerLogsResult{}, err + return nil, err } - return newContainerLogsResult(resp.Body), nil + return &containerLogsResult{ + body: resp.Body, + }, nil } -func newContainerLogsResult(rc io.ReadCloser) ContainerLogsResult { - if rc == nil { - panic("rc cannot be nil") - } - return ContainerLogsResult{ - rc: rc, - close: sync.OnceValue(rc.Close), - } +type containerLogsResult struct { + // body must be closed to avoid a resource leak + body io.ReadCloser } -// Read implements the io.Reader interface. -func (r ContainerLogsResult) Read(p []byte) (n int, err error) { - if r.rc == nil { +var ( + _ io.ReadCloser = (*containerLogsResult)(nil) + _ ContainerLogsResult = (*containerLogsResult)(nil) +) + +func (r *containerLogsResult) Read(p []byte) (int, error) { + if r == nil || r.body == nil { return 0, io.EOF } - return r.rc.Read(p) + return r.body.Read(p) } -// Close closes the underlying reader. -func (r ContainerLogsResult) Close() error { - if r.close == nil { +func (r *containerLogsResult) Close() error { + if r == nil || r.body == nil { return nil } - return r.close() + return r.body.Close() }