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:
+28
-16
@@ -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
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user