Merge pull request #51317 from austinvazquez/refactor-client-container-logs

client: refactor `ContainerLogs` to wrap result
This commit is contained in:
Austin Vazquez
2025-10-29 05:06:22 -05:00
committed by GitHub
5 changed files with 81 additions and 17 deletions

View File

@@ -63,7 +63,7 @@ type ContainerAPIClient interface {
ContainerInspect(ctx context.Context, container string, options ContainerInspectOptions) (ContainerInspectResult, error)
ContainerKill(ctx context.Context, container string, options ContainerKillOptions) (ContainerKillResult, error)
ContainerList(ctx context.Context, options ContainerListOptions) (ContainerListResult, error)
ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (io.ReadCloser, error)
ContainerLogs(ctx context.Context, container string, options ContainerLogsOptions) (ContainerLogsResult, error)
ContainerPause(ctx context.Context, container string, options ContainerPauseOptions) (ContainerPauseResult, error)
ContainerRemove(ctx context.Context, container string, options ContainerRemoveOptions) (ContainerRemoveResult, error)
ContainerRename(ctx context.Context, container, newContainerName string) error

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net/url"
"sync"
"time"
"github.com/moby/moby/client/internal/timestamp"
@@ -22,6 +23,12 @@ type ContainerLogsOptions struct {
Details bool
}
// ContainerLogsResult is the result of a container logs operation.
type ContainerLogsResult struct {
rc io.ReadCloser
close func() error
}
// ContainerLogs returns the logs generated by a container in an [io.ReadCloser].
// It's up to the caller to close the stream.
//
@@ -48,10 +55,10 @@ type ContainerLogsOptions struct {
// [stdcopy.StdType]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#StdType
// [Stdout]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stdout
// [Stderr]: https://pkg.go.dev/github.com/moby/moby/api/pkg/stdcopy#Stderr
func (cli *Client) ContainerLogs(ctx context.Context, containerID string, options ContainerLogsOptions) (io.ReadCloser, error) {
func (cli *Client) ContainerLogs(ctx context.Context, containerID string, options ContainerLogsOptions) (ContainerLogsResult, error) {
containerID, err := trimID("container", containerID)
if err != nil {
return nil, err
return ContainerLogsResult{}, err
}
query := url.Values{}
@@ -66,7 +73,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 nil, fmt.Errorf(`invalid value for "since": %w`, err)
return ContainerLogsResult{}, fmt.Errorf(`invalid value for "since": %w`, err)
}
query.Set("since", ts)
}
@@ -74,7 +81,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 nil, fmt.Errorf(`invalid value for "until": %w`, err)
return ContainerLogsResult{}, fmt.Errorf(`invalid value for "until": %w`, err)
}
query.Set("until", ts)
}
@@ -94,7 +101,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 nil, err
return ContainerLogsResult{}, err
}
return resp.Body, nil
return newContainerLogsResult(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),
}
}
// Read implements the io.Reader interface.
func (r ContainerLogsResult) Read(p []byte) (n int, err error) {
if r.rc == nil {
return 0, io.EOF
}
return r.rc.Read(p)
}
// Close closes the underlying reader.
func (r ContainerLogsResult) Close() error {
if r.close == nil {
return nil
}
return r.close()
}