Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions driver/docker-container/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import (
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/mount"
dockerclient "github.com/moby/moby/client"
"github.com/moby/moby/client/pkg/jsonmessage"
"github.com/moby/moby/client/pkg/security"
"github.com/pkg/errors"
)
Expand Down Expand Up @@ -103,8 +102,7 @@ func (d *Driver) create(ctx context.Context, l progress.SubLogger) error {
if err != nil {
return err
}
defer resp.Close()
return jsonmessage.DisplayJSONMessagesStream(resp, io.Discard, 0, false, nil)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#2988 changed this code path to explicitly read the pull JSON stream and return any errorDetail entries from the daemon.

That behavior matters because images/create can return 200 OK and then report the real failure later in the streamed JSON response, which is the bug fixed in moby/moby#49453

The current implementation still preserves that because DisplayJSONMessagesStream() returns jm.Error from the stream:

  • // Display prints the JSONMessage to out. If isTerminal is true, it erases
    // the entire current line when displaying the progressbar. It returns an
    // error if the [JSONMessage.Error] field is non-nil.
    func Display(jm jsonstream.Message, out io.Writer, isTerminal bool, width uint16) error {
    if jm.Error != nil {
    return jm.Error
    }
    var endl string
    if isTerminal && jm.Stream == "" && jm.Progress != nil {
    clearLine(out)
    endl = "\r"
    _, _ = fmt.Fprint(out, endl)
    } else if jm.Progress != nil && (jm.Progress.Current > 0 || jm.Progress.Total > 0) { // disable progressbar in non-terminal
    return nil
    }
    if jm.ID != "" {
    _, _ = fmt.Fprintf(out, "%s: ", jm.ID)
    }
    if jm.Progress != nil && isTerminal {
    if width == 0 {
    width = 200
    }
    _, _ = fmt.Fprintf(out, "%s %s%s", jm.Status, RenderTUIProgress(*jm.Progress, width), endl)
    } else if jm.Stream != "" {
    _, _ = fmt.Fprintf(out, "%s%s", jm.Stream, endl)
    } else {
    _, _ = fmt.Fprintf(out, "%s%s\n", jm.Status, endl)
    }
    return nil
  • func DisplayJSONMessages(messages iter.Seq2[jsonstream.Message, error], out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error {
    ids := make(map[string]uint)
    var width uint16 = 200
    if isTerminal {
    ws, err := term.GetWinsize(terminalFd)
    if err == nil {
    width = ws.Width
    }
    }
    for jm, err := range messages {
    var diff uint
    if err != nil {
    return err
    }

By contrast, resp.Wait(ctx) only returns decoder/context errors and does not inspect jsonstream.Message.Error at all:

  • // JSONMessages decodes the response stream as a sequence of JSONMessages.
    // if stream ends or context is cancelled, the underlying [io.Reader] is closed.
    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()
    }()
    for {
    var jm jsonstream.Message
    err := dec.Decode(&jm)
    if errors.Is(err, io.EOF) {
    break
    }
    if ctx.Err() != nil {
    yield(jm, ctx.Err())
    return
    }
    if !yield(jm, err) {
    return
    }
    }
    }
    }
    // Wait waits for operation to complete and detects errors reported as JSONMessage
    func (r Stream) Wait(ctx context.Context) error {
    for _, err := range r.JSONMessages(ctx) {
    if err != nil {
    return err
    }
    }
    return nil
  • type Message struct {
    Stream string `json:"stream,omitempty"`
    Status string `json:"status,omitempty"`
    Progress *Progress `json:"progressDetail,omitempty"`
    ID string `json:"id,omitempty"`
    Error *Error `json:"errorDetail,omitempty"`
    Aux *json.RawMessage `json:"aux,omitempty"` // Aux contains out-of-band data, such as digests for push signing and image id after building.

So this change looks like a regression of #2988: pull failures reported via streamed errorDetail would be silently ignored again, and bootstrap would fall through to a later, less accurate error.

return resp.Wait(ctx)
}); err != nil {
// image pulling failed, check if it exists in local image store.
// if not, return pulling error. otherwise log it.
Expand Down
245 changes: 0 additions & 245 deletions vendor/github.com/moby/moby/client/pkg/jsonmessage/jsonmessage.go

This file was deleted.

1 change: 0 additions & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,6 @@ github.com/moby/moby/api/types/volume
github.com/moby/moby/client
github.com/moby/moby/client/internal
github.com/moby/moby/client/internal/timestamp
github.com/moby/moby/client/pkg/jsonmessage
github.com/moby/moby/client/pkg/security
github.com/moby/moby/client/pkg/stringid
github.com/moby/moby/client/pkg/versions
Expand Down
Loading