vendor: github.com/moby/moby/client v0.4.1, moby/api v1.54.2

- https://github.com/moby/moby/compare/api/v1.54.1...api/v1.54.2
- https://github.com/moby/moby/compare/client/v0.4.0...client/v0.4.1

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
This commit is contained in:
Sebastiaan van Stijn
2026-04-21 12:16:59 +02:00
parent 478d9b19a2
commit a2e6003124
6 changed files with 108 additions and 42 deletions
+28 -16
View File
@@ -12,8 +12,9 @@ const rs = 0x1E
type DecoderFn func(v any) error
// NewJSONStreamDecoder builds adequate DecoderFn to read json records formatted with specified content-type
func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn {
// NewJSONStreamDecoder builds a DecoderFn to read a stream of JSON records
// formatted with the specified content-type.
func NewJSONStreamDecoder(r io.Reader, contentType types.MediaType) DecoderFn {
switch contentType {
case types.MediaTypeJSONSequence:
return json.NewDecoder(NewRSFilterReader(r)).Decode
@@ -24,27 +25,38 @@ func NewJSONStreamDecoder(r io.Reader, contentType string) DecoderFn {
}
}
// RSFilterReader wraps an io.Reader and filters out ASCII RS characters
type RSFilterReader struct {
type rsFilterReader struct {
reader io.Reader
buffer []byte
}
// NewRSFilterReader creates a new RSFilterReader that filters out RS characters
func NewRSFilterReader(r io.Reader) *RSFilterReader {
return &RSFilterReader{
reader: r,
buffer: make([]byte, 4096), // Internal buffer for reading chunks
}
// NewRSFilterReader creates an [io.Reader] that filters out ASCII Record Separators (RS).
func NewRSFilterReader(r io.Reader) io.Reader {
return &rsFilterReader{reader: r}
}
// Read implements the io.Reader interface, filtering out RS characters
func (r *RSFilterReader) Read(p []byte) (n int, err error) {
func (r *rsFilterReader) Read(p []byte) (int, error) {
if len(p) == 0 {
return 0, nil
}
n, err = r.reader.Read(p)
filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs })
return len(filtered), err
for {
n, err := r.reader.Read(p)
if n == 0 {
return 0, err
}
filtered := slices.DeleteFunc(p[:n], func(b byte) bool { return b == rs })
n = len(filtered)
if err != nil {
if err == io.EOF && n > 0 {
return n, nil
}
return n, err
}
if n == 0 {
// Avoid returning (0, nil) after consuming input; keep reading until data or an error (e.g., EOF).
continue
}
return n, nil
}
}
+64 -13
View File
@@ -8,6 +8,8 @@ import (
"iter"
"sync"
"github.com/containerd/errdefs/pkg/errhttp"
"github.com/moby/moby/api/types/jsonstream"
)
@@ -44,41 +46,90 @@ func (r Stream) Close() error {
var _ io.ReadCloser = Stream{}
// JSONMessages decodes the response stream as a sequence of JSONMessages.
// if stream ends or context is cancelled, the underlying [io.Reader] is closed.
// JSONMessages decodes the response stream as a sequence of [jsonstream.Message].
// The underlying [io.Reader] is closed when the stream ends or if the context
// is cancelled.
func (r Stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] {
stop := context.AfterFunc(ctx, func() {
_ = r.Close()
})
dec := json.NewDecoder(r)
return func(yield func(jsonstream.Message, error) bool) {
defer func() {
stop() // unregister AfterFunc
r.Close()
_ = r.Close()
}()
dec := json.NewDecoder(r)
for {
var jm jsonstream.Message
err := dec.Decode(&jm)
if errors.Is(err, io.EOF) {
break
}
if ctx.Err() != nil {
yield(jm, ctx.Err())
if err := dec.Decode(&jm); err != nil {
if errors.Is(err, io.EOF) {
return
}
if err := ctx.Err(); err != nil {
// Do not return decoding errors if the context was
// cancelled, because the decoding errors may be due
// to the context being cancelled.
yield(jsonstream.Message{}, err)
return
}
yield(jsonstream.Message{}, err)
return
}
if !yield(jm, err) {
if !yield(jm, nil) {
return
}
}
}
}
// Wait waits for operation to complete and detects errors reported as JSONMessage
// Wait consumes the stream until completion.
//
// It returns nil if the operation completes successfully. Errors are
// returned if the context is canceled, a decoding/transport failure
// occurs, or a JSON message reports an error ([jsonstream.Message.Error]).
func (r Stream) Wait(ctx context.Context) error {
for _, err := range r.JSONMessages(ctx) {
for jm, err := range r.JSONMessages(ctx) {
if err != nil {
// decode, transport and context cancellation errors.
return err
}
if jm.Error != nil {
// push/pull failures.
return httpErrorFromStatusCode(jm.Error, jm.Error.Code)
}
}
return nil
}
type httpError struct {
err error
errdef error
}
func (e *httpError) Error() string {
return e.err.Error()
}
func (e *httpError) Unwrap() error {
return e.err
}
func (e *httpError) Is(target error) bool {
return errors.Is(e.errdef, target)
}
// httpErrorFromStatusCode creates an errdef error, based on the provided HTTP status-code
//
// TODO(thaJeztah): unify with the implementation in client and move to an internal package
// see https://github.com/moby/moby/blob/client/v0.4.0/client/errors.go#L76-L114
func httpErrorFromStatusCode(err error, statusCode int) error {
if err == nil {
return nil
}
return &httpError{
err: err,
errdef: errhttp.ToNative(statusCode),
}
}
+8 -5
View File
@@ -244,12 +244,15 @@ func imageDiskUsageFromLegacyAPI(du *legacyDiskUsage) ImagesDiskUsage {
Items: du.Images,
}
for _, i := range idu.Items {
if i.Containers > 0 {
for _, img := range idu.Items {
switch {
case img.Containers < 0:
// No container-count information available; skip (assume it's in use).
case img.Containers > 0:
idu.ActiveCount++
} else if i.Size != -1 && i.SharedSize != -1 {
// Only count reclaimable size if we have size information
idu.Reclaimable += (i.Size - i.SharedSize)
case img.Containers == 0 && img.Size != -1 && img.SharedSize != -1:
reclaimable := img.Size - img.SharedSize
idu.Reclaimable += reclaimable
}
}