Files
monok8s/clitools/pkg/controller/osimage/progress.go
2026-04-04 02:45:46 +08:00

122 lines
2.3 KiB
Go

package osimage
import (
"k8s.io/klog/v2"
"time"
)
type progressState struct {
lastTime time.Time
lastPercent int64
lastBucket int64
}
type ProgressLogger struct {
minInterval time.Duration
bucketSize int64
states map[string]*progressState
}
func NewProgressLogger(minSeconds int, bucketSize int64) *ProgressLogger {
if minSeconds < 0 {
minSeconds = 0
}
if bucketSize <= 0 {
bucketSize = 10
}
return &ProgressLogger{
minInterval: time.Duration(minSeconds) * time.Second,
bucketSize: bucketSize,
states: make(map[string]*progressState),
}
}
func (l *ProgressLogger) state(stage string) *progressState {
s, ok := l.states[stage]
if ok {
return s
}
s = &progressState{
lastPercent: -1,
lastBucket: -1,
}
l.states[stage] = s
return s
}
func (l *ProgressLogger) Log(p Progress) {
if p.BytesTotal <= 0 {
return
}
percent := PercentOf(p.BytesComplete, p.BytesTotal)
now := time.Now()
bucket := percent / l.bucketSize
s := l.state(p.Stage)
// Always log first visible progress
if s.lastPercent == -1 {
s.lastPercent = percent
s.lastBucket = bucket
s.lastTime = now
klog.V(4).InfoS(p.Stage, "progress", percent)
return
}
// Always log completion once
if percent == 100 && s.lastPercent < 100 {
s.lastPercent = 100
s.lastBucket = 100 / l.bucketSize
s.lastTime = now
klog.V(4).InfoS(p.Stage, "progress", 100)
return
}
// Log if we crossed a new milestone bucket
if bucket > s.lastBucket {
s.lastPercent = percent
s.lastBucket = bucket
s.lastTime = now
klog.V(4).InfoS(p.Stage, "progress", percent)
return
}
// Otherwise allow a timed refresh if progress moved
if now.Sub(s.lastTime) >= l.minInterval && percent > s.lastPercent {
s.lastPercent = percent
s.lastTime = now
klog.V(4).InfoS(p.Stage, "progress", percent)
}
}
type TimeBasedUpdater struct {
interval time.Duration
lastRun time.Time
}
func NewTimeBasedUpdater(seconds int) *TimeBasedUpdater {
if seconds <= 0 {
seconds = 15
}
return &TimeBasedUpdater{
interval: time.Duration(seconds) * time.Second,
}
}
func (u *TimeBasedUpdater) Run(fn func() error) error {
now := time.Now()
if !u.lastRun.IsZero() && now.Sub(u.lastRun) < u.interval {
return nil
}
if err := fn(); err != nil {
return err
}
u.lastRun = now
return nil
}