/
pull_status.go
138 lines (116 loc) · 3.19 KB
/
pull_status.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package docker
import (
"sync"
"github.com/wagoodman/go-progress"
)
const (
UnknownPhase PullPhase = iota
WaitingPhase
PullingFsPhase
DownloadingPhase
DownloadCompletePhase
ExtractingPhase
VerifyingChecksumPhase
AlreadyExistsPhase
PullCompletePhase
)
var phaseLookup = map[string]PullPhase{
"Waiting": WaitingPhase,
"Pulling fs layer": PullingFsPhase,
"Downloading": DownloadingPhase,
"Download complete": DownloadCompletePhase,
"Extracting": ExtractingPhase,
"Verifying Checksum": VerifyingChecksumPhase,
"Already exists": AlreadyExistsPhase,
"Pull complete": PullCompletePhase,
}
type PullPhase int
type LayerID string
type pullEvent struct {
ID string `json:"id"`
Status string `json:"status"`
Error string `json:"error,omitempty"`
Progress string `json:"progress,omitempty"`
ProgressDetail struct {
Current int `json:"current"`
Total int `json:"total"`
} `json:"progressDetail"`
}
type LayerState struct {
Phase PullPhase
PhaseProgress progress.Progressable
DownloadProgress progress.Progressable
}
type PullStatus struct {
phaseProgress map[LayerID]*progress.Manual
downloadProgress map[LayerID]*progress.Manual
phase map[LayerID]PullPhase
layers []LayerID
lock sync.Mutex
complete bool
}
func newPullStatus() *PullStatus {
return &PullStatus{
phaseProgress: make(map[LayerID]*progress.Manual),
downloadProgress: make(map[LayerID]*progress.Manual),
phase: make(map[LayerID]PullPhase),
}
}
func (p *PullStatus) Complete() bool {
return p.complete
}
func (p *PullStatus) Layers() []LayerID {
p.lock.Lock()
defer p.lock.Unlock()
return append([]LayerID{}, p.layers...)
}
func (p *PullStatus) Current(layer LayerID) LayerState {
p.lock.Lock()
defer p.lock.Unlock()
return LayerState{
Phase: p.phase[layer],
PhaseProgress: progress.Progressable(p.phaseProgress[layer]),
DownloadProgress: progress.Progressable(p.downloadProgress[layer]),
}
}
func (p *PullStatus) onEvent(event *pullEvent) {
p.lock.Lock()
defer p.lock.Unlock()
layer := LayerID(event.ID)
if layer == "" {
return
}
if _, ok := p.phaseProgress[layer]; !ok {
// ignore the first layer as it's the image id
if p.layers == nil {
p.layers = make([]LayerID, 0)
return
}
// this is a new layer, initialize tracking info
p.phaseProgress[layer] = &progress.Manual{}
p.downloadProgress[layer] = &progress.Manual{}
p.layers = append(p.layers, layer)
}
// capture latest event info
currentPhase, ok := phaseLookup[event.Status]
if !ok {
currentPhase = UnknownPhase
}
p.phase[layer] = currentPhase
phaseProgress := p.phaseProgress[layer]
if currentPhase >= AlreadyExistsPhase {
phaseProgress.SetCompleted()
} else {
phaseProgress.Set(int64(event.ProgressDetail.Current))
phaseProgress.SetTotal(int64(event.ProgressDetail.Total))
}
if currentPhase == DownloadingPhase {
dl := p.downloadProgress[layer]
dl.Set(int64(event.ProgressDetail.Current))
dl.SetTotal(int64(event.ProgressDetail.Total))
} else if currentPhase >= DownloadCompletePhase {
dl := p.downloadProgress[layer]
dl.Set(dl.Size())
dl.SetCompleted()
}
}