mirror of
https://github.com/siderolabs/talos.git
synced 2026-04-16 03:01:08 +02:00
This reports image pull progress in the console for images pulled by Talos: * etcd * kubelet * installer This work was mostly done by @laurazard, I just wrapped it for the console with Laura's help. (see #12932) Co-authored-by: Laura Brehm <laurabrehm@hey.com> Signed-off-by: Andrey Smirnov <andrey.smirnov@siderolabs.com>
139 lines
2.8 KiB
Go
139 lines
2.8 KiB
Go
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
// Package console provides a console-based implementation of image pull progress reporting.
|
|
package console
|
|
|
|
import (
|
|
"log"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/dustin/go-humanize"
|
|
|
|
"github.com/siderolabs/talos/internal/pkg/containers/image"
|
|
"github.com/siderolabs/talos/internal/pkg/containers/image/progress"
|
|
)
|
|
|
|
// ReportInterval is the interval between progress reports.
|
|
const ReportInterval = 15 * time.Second
|
|
|
|
type layerProgress struct {
|
|
status progress.LayerPullStatus
|
|
offset int64
|
|
total int64
|
|
}
|
|
|
|
// ProgressReporter reports image pull progress to the console.
|
|
type ProgressReporter struct {
|
|
imageRef string
|
|
|
|
mu sync.Mutex
|
|
layers map[string]*layerProgress
|
|
stopCh chan struct{}
|
|
}
|
|
|
|
// NewProgressReporter creates a new ProgressReporter.
|
|
func NewProgressReporter(imageRef string) image.ProgressReporter {
|
|
return &ProgressReporter{
|
|
imageRef: imageRef,
|
|
}
|
|
}
|
|
|
|
// Update implements UpdateFn interface.
|
|
func (c *ProgressReporter) Update(upd progress.LayerPullProgress) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if c.layers == nil {
|
|
c.layers = make(map[string]*layerProgress)
|
|
}
|
|
|
|
lp, ok := c.layers[upd.LayerID]
|
|
if !ok {
|
|
lp = &layerProgress{}
|
|
c.layers[upd.LayerID] = lp
|
|
}
|
|
|
|
if upd.Status == progress.LayerPullStatusDownloading {
|
|
lp.total = upd.Total
|
|
lp.offset = upd.Offset
|
|
} else {
|
|
lp.offset = lp.total
|
|
}
|
|
|
|
lp.status = upd.Status
|
|
}
|
|
|
|
// Start implements ProgressReporter interface.
|
|
func (c *ProgressReporter) Start() {
|
|
c.stopCh = make(chan struct{})
|
|
|
|
go func() {
|
|
ticker := time.NewTicker(ReportInterval)
|
|
defer ticker.Stop()
|
|
|
|
c.reportProgress()
|
|
|
|
for {
|
|
select {
|
|
case <-ticker.C:
|
|
c.reportProgress()
|
|
case <-c.stopCh:
|
|
return
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Stop implements ProgressReporter interface.
|
|
func (c *ProgressReporter) Stop() {
|
|
close(c.stopCh)
|
|
}
|
|
|
|
func (c *ProgressReporter) reportProgress() {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
if len(c.layers) == 0 {
|
|
log.Printf("pulling image %s: starting...", c.imageRef)
|
|
|
|
return
|
|
}
|
|
|
|
var (
|
|
anyDownloading bool
|
|
overallOffset int64
|
|
overallTotal int64
|
|
)
|
|
|
|
for _, l := range c.layers {
|
|
if l.status == progress.LayerPullStatusDownloading {
|
|
anyDownloading = true
|
|
}
|
|
|
|
overallOffset += l.offset
|
|
overallTotal += l.total
|
|
}
|
|
|
|
if !anyDownloading {
|
|
log.Printf("pulling image %s: extracting...", c.imageRef)
|
|
|
|
return
|
|
}
|
|
|
|
var percentage float64
|
|
|
|
if overallTotal > 0 {
|
|
percentage = float64(overallOffset) / float64(overallTotal) * 100.0
|
|
}
|
|
|
|
log.Printf("pulling image %s: downloading %d layers (%s/%s) (%.2f%%)...",
|
|
c.imageRef, len(c.layers),
|
|
humanize.IBytes(uint64(overallOffset)),
|
|
humanize.IBytes(uint64(overallTotal)),
|
|
percentage,
|
|
)
|
|
}
|