Skip to content

Commit 5a871b8

Browse files
authored
Merge pull request moby#51156 from corhere/client-pkg-jsonmessage
client/pkg/jsonmessage: refactor in terms of `api/types/jsonstream`
2 parents 72f6cec + bbaeb90 commit 5a871b8

File tree

17 files changed

+110
-188
lines changed

17 files changed

+110
-188
lines changed

client/image_pull.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ import (
99

1010
cerrdefs "github.com/containerd/errdefs"
1111
"github.com/distribution/reference"
12+
"github.com/moby/moby/api/types/jsonstream"
1213
"github.com/moby/moby/client/internal"
13-
"github.com/moby/moby/client/pkg/jsonmessage"
1414
)
1515

1616
type ImagePullResponse interface {
1717
io.ReadCloser
18-
JSONMessages(ctx context.Context) iter.Seq2[jsonmessage.JSONMessage, error]
18+
JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error]
1919
Wait(ctx context.Context) error
2020
}
2121

client/image_pull_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ import (
1010
"time"
1111

1212
cerrdefs "github.com/containerd/errdefs"
13+
"github.com/moby/moby/api/types/jsonstream"
1314
"github.com/moby/moby/api/types/registry"
1415
"github.com/moby/moby/client/internal"
15-
"github.com/moby/moby/client/pkg/jsonmessage"
1616
"gotest.tools/v3/assert"
1717
is "gotest.tools/v3/assert/cmp"
1818
)
@@ -193,7 +193,7 @@ func TestImagePullResponse(t *testing.T) {
193193
response := internal.NewJSONMessageStream(r)
194194
ctx, cancel := context.WithCancel(t.Context())
195195
messages := response.JSONMessages(ctx)
196-
c := make(chan jsonmessage.JSONMessage)
196+
c := make(chan jsonstream.Message)
197197
go func() {
198198
for message, err := range messages {
199199
if err != nil {

client/image_push.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@ import (
1212

1313
cerrdefs "github.com/containerd/errdefs"
1414
"github.com/distribution/reference"
15+
"github.com/moby/moby/api/types/jsonstream"
1516
"github.com/moby/moby/api/types/registry"
1617
"github.com/moby/moby/client/internal"
17-
"github.com/moby/moby/client/pkg/jsonmessage"
1818
)
1919

2020
type ImagePushResponse interface {
2121
io.ReadCloser
22-
JSONMessages(ctx context.Context) iter.Seq2[jsonmessage.JSONMessage, error]
22+
JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error]
2323
Wait(ctx context.Context) error
2424
}
2525

client/internal/jsonmessages.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"iter"
99
"sync"
1010

11-
"github.com/moby/moby/client/pkg/jsonmessage"
11+
"github.com/moby/moby/api/types/jsonstream"
1212
)
1313

1414
func NewJSONMessageStream(rc io.ReadCloser) stream {
@@ -44,15 +44,15 @@ func (r stream) Close() error {
4444

4545
// JSONMessages decodes the response stream as a sequence of JSONMessages.
4646
// if stream ends or context is cancelled, the underlying [io.Reader] is closed.
47-
func (r stream) JSONMessages(ctx context.Context) iter.Seq2[jsonmessage.JSONMessage, error] {
47+
func (r stream) JSONMessages(ctx context.Context) iter.Seq2[jsonstream.Message, error] {
4848
context.AfterFunc(ctx, func() {
4949
_ = r.Close()
5050
})
5151
dec := json.NewDecoder(r)
52-
return func(yield func(jsonmessage.JSONMessage, error) bool) {
52+
return func(yield func(jsonstream.Message, error) bool) {
5353
defer r.Close()
5454
for {
55-
var jm jsonmessage.JSONMessage
55+
var jm jsonstream.Message
5656
err := dec.Decode(&jm)
5757
if errors.Is(err, io.EOF) {
5858
break

client/pkg/jsonmessage/jsonmessage.go

Lines changed: 23 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,14 @@ import (
1414
"github.com/moby/term"
1515
)
1616

17+
var timeNow = time.Now // For overriding in tests.
18+
1719
// RFC3339NanoFixed is time.RFC3339Nano with nanoseconds padded using zeros to
1820
// ensure the formatted time isalways the same number of characters.
1921
const RFC3339NanoFixed = "2006-01-02T15:04:05.000000000Z07:00"
2022

21-
// JSONProgress describes a progress message in a JSON stream.
22-
type JSONProgress struct {
23-
jsonstream.Progress
24-
25-
// terminalFd is the fd of the current terminal, if any. It is used
26-
// to get the terminal width.
27-
terminalFd uintptr
28-
29-
// nowFunc is used to override the current time in tests.
30-
nowFunc func() time.Time
31-
32-
// winSize is used to override the terminal width in tests.
33-
winSize int
34-
}
35-
36-
func (p *JSONProgress) String() string {
23+
func RenderTUIProgress(p jsonstream.Progress, width uint16) string {
3724
var (
38-
width = p.width()
3925
pbBox string
4026
numbersBox string
4127
)
@@ -89,7 +75,7 @@ func (p *JSONProgress) String() string {
8975
var timeLeftBox string
9076
if width > 50 {
9177
if p.Current > 0 && p.Start > 0 && percentage < 50 {
92-
fromStart := p.now().Sub(time.Unix(p.Start, 0))
78+
fromStart := timeNow().UTC().Sub(time.Unix(p.Start, 0))
9379
perEntry := fromStart / time.Duration(p.Current)
9480
left := time.Duration(p.Total-p.Current) * perEntry
9581
timeLeftBox = " " + left.Round(time.Second).String()
@@ -98,40 +84,6 @@ func (p *JSONProgress) String() string {
9884
return pbBox + numbersBox + timeLeftBox
9985
}
10086

101-
// now returns the current time in UTC, but can be overridden in tests
102-
// by setting JSONProgress.nowFunc to a custom function.
103-
func (p *JSONProgress) now() time.Time {
104-
if p.nowFunc != nil {
105-
return p.nowFunc()
106-
}
107-
return time.Now().UTC()
108-
}
109-
110-
// width returns the current terminal's width, but can be overridden
111-
// in tests by setting JSONProgress.winSize to a non-zero value.
112-
func (p *JSONProgress) width() int {
113-
if p.winSize != 0 {
114-
return p.winSize
115-
}
116-
ws, err := term.GetWinsize(p.terminalFd)
117-
if err == nil {
118-
return int(ws.Width)
119-
}
120-
return 200
121-
}
122-
123-
// JSONMessage defines a message struct. It describes
124-
// the created time, where it from, status, ID of the
125-
// message. It's used for docker events.
126-
type JSONMessage struct {
127-
Stream string `json:"stream,omitempty"`
128-
Status string `json:"status,omitempty"`
129-
Progress *JSONProgress `json:"progressDetail,omitempty"`
130-
ID string `json:"id,omitempty"`
131-
Error *jsonstream.Error `json:"errorDetail,omitempty"`
132-
Aux *json.RawMessage `json:"aux,omitempty"` // Aux contains out-of-band data, such as digests for push signing and image id after building.
133-
}
134-
13587
// We can probably use [aec.EmptyBuilder] for managing the output, but
13688
// currently we're doing it all manually, so defining some consts for
13789
// the basics we use.
@@ -164,7 +116,7 @@ func cursorDown(out io.Writer, l uint) {
164116
// Display prints the JSONMessage to out. If isTerminal is true, it erases
165117
// the entire current line when displaying the progressbar. It returns an
166118
// error if the [JSONMessage.Error] field is non-nil.
167-
func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
119+
func Display(jm jsonstream.Message, out io.Writer, isTerminal bool, width uint16) error {
168120
if jm.Error != nil {
169121
return jm.Error
170122
}
@@ -173,14 +125,17 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
173125
clearLine(out)
174126
endl = "\r"
175127
_, _ = fmt.Fprint(out, endl)
176-
} else if jm.Progress != nil && jm.Progress.String() != "" { // disable progressbar in non-terminal
128+
} else if jm.Progress != nil && (jm.Progress.Current > 0 || jm.Progress.Total > 0) { // disable progressbar in non-terminal
177129
return nil
178130
}
179131
if jm.ID != "" {
180132
_, _ = fmt.Fprintf(out, "%s: ", jm.ID)
181133
}
182134
if jm.Progress != nil && isTerminal {
183-
_, _ = fmt.Fprintf(out, "%s %s%s", jm.Status, jm.Progress.String(), endl)
135+
if width == 0 {
136+
width = 200
137+
}
138+
_, _ = fmt.Fprintf(out, "%s %s%s", jm.Status, RenderTUIProgress(*jm.Progress, width), endl)
184139
} else if jm.Stream != "" {
185140
_, _ = fmt.Fprintf(out, "%s%s", jm.Stream, endl)
186141
} else {
@@ -189,16 +144,16 @@ func (jm *JSONMessage) Display(out io.Writer, isTerminal bool) error {
189144
return nil
190145
}
191146

192-
type JSONMessagesStream iter.Seq2[JSONMessage, error]
147+
type JSONMessagesStream iter.Seq2[jsonstream.Message, error]
193148

194149
// DisplayJSONMessagesStream reads a JSON message stream from in, and writes
195150
// each [JSONMessage] to out.
196151
// see DisplayJSONMessages for details
197-
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
152+
func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error {
198153
dec := json.NewDecoder(in)
199-
var f JSONMessagesStream = func(yield func(JSONMessage, error) bool) {
154+
var f JSONMessagesStream = func(yield func(jsonstream.Message, error) bool) {
200155
for {
201-
var jm JSONMessage
156+
var jm jsonstream.Message
202157
err := dec.Decode(&jm)
203158
if errors.Is(err, io.EOF) {
204159
break
@@ -228,8 +183,15 @@ func DisplayJSONMessagesStream(in io.Reader, out io.Writer, terminalFd uintptr,
228183
// - auxCallback allows handling the [JSONMessage.Aux] field. It is
229184
// called if a JSONMessage contains an Aux field, in which case
230185
// DisplayJSONMessagesStream does not present the JSONMessage.
231-
func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(JSONMessage)) error {
186+
func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd uintptr, isTerminal bool, auxCallback func(jsonstream.Message)) error {
232187
ids := make(map[string]uint)
188+
var width uint16 = 200
189+
if isTerminal {
190+
ws, err := term.GetWinsize(terminalFd)
191+
if err == nil {
192+
width = ws.Width
193+
}
194+
}
233195

234196
for jm, err := range messages {
235197
var diff uint
@@ -244,9 +206,6 @@ func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd
244206
continue
245207
}
246208

247-
if jm.Progress != nil {
248-
jm.Progress.terminalFd = terminalFd
249-
}
250209
if jm.ID != "" && jm.Progress != nil {
251210
line, ok := ids[jm.ID]
252211
if !ok {
@@ -274,7 +233,7 @@ func DisplayJSONMessages(messages JSONMessagesStream, out io.Writer, terminalFd
274233
// with multiple tags).
275234
ids = make(map[string]uint)
276235
}
277-
err := jm.Display(out, isTerminal)
236+
err := Display(jm, out, isTerminal, width)
278237
if jm.ID != "" && isTerminal {
279238
cursorDown(out, diff)
280239
}

0 commit comments

Comments
 (0)