-
Couldn't load subscription status.
- Fork 51
Add pull to docker daemon image provider #35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,6 +3,8 @@ package parsers | |
| import ( | ||
| "fmt" | ||
|
|
||
| "github.com/anchore/stereoscope/pkg/image/docker" | ||
|
|
||
| "github.com/anchore/stereoscope/pkg/event" | ||
| "github.com/anchore/stereoscope/pkg/image" | ||
| "github.com/wagoodman/go-partybus" | ||
|
|
@@ -34,6 +36,24 @@ func checkEventType(actual, expected partybus.EventType) error { | |
| return nil | ||
| } | ||
|
|
||
| func ParsePullDockerImage(e partybus.Event) (string, *docker.PullStatus, error) { | ||
| if err := checkEventType(e.Type, event.PullDockerImage); err != nil { | ||
| return "", nil, err | ||
| } | ||
|
|
||
| imgName, ok := e.Source.(string) | ||
| if !ok { | ||
| return "", nil, newPayloadErr(e.Type, "Source", e.Source) | ||
| } | ||
|
|
||
| prog, ok := e.Value.(*docker.PullStatus) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider renaming There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it is a reference to a object which the consumer can poll for status continually |
||
| if !ok { | ||
| return "", nil, newPayloadErr(e.Type, "Value", e.Value) | ||
| } | ||
|
|
||
| return imgName, prog, nil | ||
| } | ||
|
|
||
| func ParseFetchImage(e partybus.Event) (string, progress.StagedProgressable, error) { | ||
| if err := checkEventType(e.Type, event.FetchImage); err != nil { | ||
| return "", nil, err | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| package docker | ||
|
|
||
| import ( | ||
| "sync" | ||
|
|
||
| "github.com/wagoodman/go-progress" | ||
| ) | ||
|
|
||
| const ( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the case for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. to summarize our conversation: I'll leave the type of PullPhase alone and I'll translate the existing parse function for a map |
||
| UnknownPhase PullPhase = iota | ||
| WaitingPhase | ||
| PullingFsPhase | ||
| DownloadingPhase | ||
| DownloadCompletePhase | ||
| ExtractingPhase | ||
| VerifyingChecksumPhase | ||
| AlreadyExistsPhase | ||
| 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 := parsePhase(event.Status) | ||
| p.phase[layer] = currentPhase | ||
| phaseProgress := p.phaseProgress[layer] | ||
|
|
||
| if currentPhase >= AlreadyExistsPhase { | ||
| phaseProgress.SetCompleted() | ||
| } else { | ||
| phaseProgress.N = int64(event.ProgressDetail.Current) | ||
| phaseProgress.Total = int64(event.ProgressDetail.Total) | ||
| } | ||
|
|
||
| if currentPhase == DownloadingPhase { | ||
| dl := p.downloadProgress[layer] | ||
| dl.N = int64(event.ProgressDetail.Current) | ||
| dl.Total = int64(event.ProgressDetail.Total) | ||
| } else if currentPhase >= DownloadCompletePhase { | ||
| dl := p.downloadProgress[layer] | ||
| dl.N = dl.Total | ||
| dl.SetCompleted() | ||
| } | ||
| } | ||
|
|
||
| func parsePhase(inputStr string) PullPhase { | ||
| switch inputStr { | ||
| case "Waiting": | ||
| return WaitingPhase | ||
| case "Pulling fs layer": | ||
| return PullingFsPhase | ||
| case "Downloading": | ||
| return DownloadingPhase | ||
| case "Download complete": | ||
| return DownloadCompletePhase | ||
| case "Extracting": | ||
| return ExtractingPhase | ||
| case "Verifying Checksum": | ||
| return VerifyingChecksumPhase | ||
| case "Already exists": | ||
| return AlreadyExistsPhase | ||
| case "Pull complete": | ||
| return PullCompletePhase | ||
| } | ||
| return UnknownPhase | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is an exported lib function, it'd be good to write a short explanation of what the function does as a doc comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll follow up in general with better docs for all of these functions