Skip to content

Commit a74bfc3

Browse files
committed
Fix image pull progress when reported in seconds
Newer Docker daemon versions (tested with 29.5.2) report layer extraction progress using only elapsed seconds instead of current and total bytes. ```json {"status":"Extracting","progressDetail":{"current":3,"units":"s"},"id":"9cb31e2e37ea"} ``` In that case, we will now ignore the layer extraction progress (because we cannot display per-layer seconds, and they are not very useful) and only include the layer's full size in the `extracted_bytes` counter when the layer completes extraction. The pull progress indicator in the CLI still looks the same, but the extraction counter is less granular with newer Docker versions than with older ones.
1 parent e649569 commit a74bfc3

2 files changed

Lines changed: 53 additions & 12 deletions

File tree

runner/internal/shim/docker.go

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,26 @@ const (
5656
// https://github.com/moby/moby/blob/e77ff99ede5ee5952b3a9227863552ae6e5b6fb1/pkg/jsonmessage/jsonmessage.go#L144
5757
// All fields are optional.
5858
type PullMessage struct {
59-
Id string `json:"id"` // layer id
60-
Status string `json:"status"`
61-
ProgressDetail struct {
62-
Current uint64 `json:"current"` // bytes
63-
Total uint64 `json:"total"` // bytes
64-
} `json:"progressDetail"`
65-
ErrorDetail struct {
59+
Id string `json:"id"` // layer id
60+
Status string `json:"status"`
61+
ProgressDetail ProgressDetail `json:"progressDetail"`
62+
ErrorDetail struct {
6663
Message string `json:"message"`
6764
} `json:"errorDetail"`
6865
}
6966

67+
type ProgressDetail struct {
68+
Current uint64 `json:"current"`
69+
Total uint64 `json:"total"`
70+
Units string `json:"units"`
71+
}
72+
73+
func (p *ProgressDetail) isUnitBytes() bool {
74+
// > Units is the unit to print for progress. It defaults to "bytes" if empty
75+
// https://github.com/moby/moby/blob/8151a55a776f5f83f68bcf0030c19031439ea357/api/types/jsonstream/progress.go#L9
76+
return p.Units == "bytes" || p.Units == ""
77+
}
78+
7079
type layerProgress struct {
7180
Status string
7281
DownloadedBytes uint64
@@ -94,14 +103,18 @@ func (t *PullTracker) Update(msg PullMessage) {
94103
case "Pulling fs layer", "Waiting", "Verifying Checksum", "Already exists":
95104
// no bytes to update, just track status
96105
case "Downloading":
97-
layer.DownloadedBytes = msg.ProgressDetail.Current
98-
layer.TotalBytes = msg.ProgressDetail.Total
106+
if msg.ProgressDetail.isUnitBytes() {
107+
layer.DownloadedBytes = msg.ProgressDetail.Current
108+
layer.TotalBytes = msg.ProgressDetail.Total
109+
}
99110
case "Download complete":
100111
layer.DownloadedBytes = layer.TotalBytes
101112
case "Extracting":
102-
layer.ExtractedBytes = msg.ProgressDetail.Current
103-
layer.DownloadedBytes = msg.ProgressDetail.Total
104-
layer.TotalBytes = msg.ProgressDetail.Total
113+
if msg.ProgressDetail.isUnitBytes() {
114+
layer.ExtractedBytes = msg.ProgressDetail.Current
115+
layer.DownloadedBytes = msg.ProgressDetail.Total
116+
layer.TotalBytes = msg.ProgressDetail.Total
117+
}
105118
case "Pull complete":
106119
layer.ExtractedBytes = layer.TotalBytes
107120
layer.DownloadedBytes = layer.TotalBytes

runner/internal/shim/docker_test.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,3 +268,31 @@ func TestPullTracker_MixedLayerStatuses(t *testing.T) {
268268
assert.Equal(t, uint64(100+200), p.TotalBytes)
269269
assert.False(t, p.IsTotalBytesFinal) // layer-waiting size unknown
270270
}
271+
272+
func TestPullTracker_NonBytesExtractingUnit(t *testing.T) {
273+
tracker := newPullTracker()
274+
tracker.Update(PullMessage{Id: "3.11", Status: "Pulling from library/python"})
275+
tracker.Update(PullMessage{Id: "aaa", Status: "Pulling fs layer"})
276+
tracker.Update(pullMsg("aaa", "Downloading", 100, 200))
277+
tracker.Update(pullMsg("aaa", "Downloading", 200, 200))
278+
tracker.Update(PullMessage{Id: "aaa", Status: "Download complete"})
279+
// Newer Docker daemons report extraction progress in seconds. Tested with 29.5.2
280+
tracker.Update(PullMessage{Id: "aaa", Status: "Extracting", ProgressDetail: ProgressDetail{Current: 1, Units: "s"}})
281+
tracker.Update(PullMessage{Id: "aaa", Status: "Extracting", ProgressDetail: ProgressDetail{Current: 2, Units: "s"}})
282+
283+
p := tracker.Progress()
284+
require.NotNil(t, p)
285+
assert.Equal(t, uint64(200), p.DownloadedBytes)
286+
assert.Equal(t, uint64(0), p.ExtractedBytes) // reported in seconds, bytes unknown
287+
assert.Equal(t, uint64(200), p.TotalBytes)
288+
assert.True(t, p.IsTotalBytesFinal)
289+
290+
tracker.Update(PullMessage{Id: "aaa", Status: "Pull complete"})
291+
292+
p = tracker.Progress()
293+
require.NotNil(t, p)
294+
assert.Equal(t, uint64(200), p.DownloadedBytes)
295+
assert.Equal(t, uint64(200), p.ExtractedBytes) // pull complete => extracted == total
296+
assert.Equal(t, uint64(200), p.TotalBytes)
297+
assert.True(t, p.IsTotalBytesFinal)
298+
}

0 commit comments

Comments
 (0)