Skip to content

Commit 0c2a6bb

Browse files
authored
fix(core): fix importer progress display error (#905)
Signed-off-by: Valeriy Khorunzhin <valeriy.khorunzhin@flant.com>
1 parent 565e768 commit 0c2a6bb

3 files changed

Lines changed: 125 additions & 13 deletions

File tree

images/dvcr-artifact/pkg/monitoring/progress.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package monitoring
1818

1919
import (
20+
"context"
2021
"errors"
2122
"fmt"
2223
"io"
@@ -27,7 +28,6 @@ import (
2728
"k8s.io/klog/v2"
2829
"kubevirt.io/containerized-data-importer/pkg/common"
2930
"kubevirt.io/containerized-data-importer/pkg/util"
30-
prometheusutil "kubevirt.io/containerized-data-importer/pkg/util/prometheus"
3131
)
3232

3333
const (
@@ -40,19 +40,16 @@ const (
4040
)
4141

4242
type ProgressMeter struct {
43-
*prometheusutil.ProgressReader
44-
43+
*ProgressReader
4544
total uint64
4645
avgSpeed ProgressMetric
4746
curSpeed ProgressMetric
4847
startedAt time.Time
4948
stoppedAt time.Time
5049
prevTransmittedBytes float64
51-
52-
finalAvgSpeed float64
53-
54-
emitInterval time.Duration
55-
stop chan struct{}
50+
emitInterval time.Duration
51+
stop chan struct{}
52+
cancel context.CancelFunc
5653
}
5754

5855
// NewProgressMeter returns reader that will track bytes count into prometheus metric.
@@ -121,9 +118,8 @@ func NewProgressMeter(rdr io.ReadCloser, total uint64) *ProgressMeter {
121118
}
122119

123120
importProgress := NewProgress(registryProgress, ownerUID)
124-
125121
return &ProgressMeter{
126-
ProgressReader: prometheusutil.NewProgressReader(rdr, importProgress, total),
122+
ProgressReader: NewProgressReader(rdr, importProgress, total),
127123
total: total,
128124
avgSpeed: NewProgress(registryAvgSpeed, ownerUID),
129125
curSpeed: NewProgress(registryCurSpeed, ownerUID),
@@ -133,12 +129,17 @@ func NewProgressMeter(rdr io.ReadCloser, total uint64) *ProgressMeter {
133129
}
134130

135131
func (p *ProgressMeter) Start() {
136-
p.ProgressReader.StartTimedUpdate()
132+
var ctx context.Context
133+
ctx, p.cancel = context.WithCancel(context.Background())
134+
p.ProgressReader.StartTimedUpdate(ctx)
137135
p.startedAt = time.Now()
138136

139137
go func() {
140138
ticker := time.NewTicker(p.emitInterval)
141-
defer ticker.Stop()
139+
defer func() {
140+
p.cancel()
141+
ticker.Stop()
142+
}()
142143

143144
for {
144145
select {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
Copyright 2025 Flant JSC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
// To display the loading progress, code from CDI
18+
// (https://github.com/kubevirt/containerized-data-importer/blob/main/pkg/util/prometheus/prometheus.go)
19+
// was used. However, due to our modifications, we could not utilize the embedding
20+
// mechanism, so we had to resort to copying.
21+
22+
package monitoring
23+
24+
import (
25+
"context"
26+
"fmt"
27+
"io"
28+
"time"
29+
30+
"k8s.io/klog/v2"
31+
32+
"kubevirt.io/containerized-data-importer/pkg/util"
33+
)
34+
35+
// ProgressReader is a counting reader that reports progress to prometheus.
36+
type ProgressReader struct {
37+
util.CountingReader
38+
metric ProgressMetric
39+
total uint64
40+
final bool
41+
}
42+
43+
// NewProgressReader creates a new instance of a prometheus updating progress reader.
44+
func NewProgressReader(r io.ReadCloser, metric ProgressMetric, total uint64) *ProgressReader {
45+
promReader := &ProgressReader{
46+
CountingReader: util.CountingReader{
47+
Reader: r,
48+
Current: 0,
49+
},
50+
metric: metric,
51+
total: total,
52+
final: true,
53+
}
54+
55+
return promReader
56+
}
57+
58+
// StartTimedUpdate starts the update timer to automatically update every second.
59+
func (r *ProgressReader) StartTimedUpdate(ctx context.Context) {
60+
// Start the progress update thread.
61+
go r.timedUpdateProgress(ctx)
62+
}
63+
64+
func (r *ProgressReader) timedUpdateProgress(ctx context.Context) {
65+
for {
66+
select {
67+
case <-ctx.Done():
68+
return
69+
case <-time.After(time.Second):
70+
cont := r.updateProgress()
71+
if !cont {
72+
return
73+
}
74+
}
75+
}
76+
}
77+
78+
func (r *ProgressReader) updateProgress() bool {
79+
if r.total > 0 {
80+
finished := r.final && r.Done
81+
currentProgress := 100.0
82+
if !finished && r.Current < r.total {
83+
currentProgress = float64(r.Current) / float64(r.total) * 100.0
84+
}
85+
progress, err := r.metric.Get()
86+
if err != nil {
87+
klog.Errorf("updateProgress: failed to read metric; %v", err)
88+
return true // true ==> to try again // todo - how to avoid endless loop in case it's a constant error?
89+
}
90+
if currentProgress > progress {
91+
r.metric.Add(currentProgress - progress)
92+
}
93+
klog.V(1).Infoln(fmt.Sprintf("%.2f", currentProgress))
94+
return !finished
95+
}
96+
return false
97+
}

images/dvcr-artifact/pkg/retry/backoff.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"context"
2121
"fmt"
2222
"math/rand"
23+
"strings"
2324
"time"
2425

2526
"k8s.io/klog/v2"
@@ -104,12 +105,25 @@ func (b *Backoff) Step() time.Duration {
104105
// In case (1) the returned error is what the condition function returned.
105106
// In all other cases, ErrWaitTimeout is returned.
106107
func ExponentialBackoff(ctx context.Context, f Fn, backoff Backoff) error {
108+
const (
109+
dvcrNoSpaceError = "no space left on device"
110+
dvcrInternalErrorPattern = "UNKNOWN: unknown error;"
111+
dvcrNoSpaceErrMessage = "DVCR is overloaded"
112+
internalDvcrErrMessage = "Internal DVCR error (could it be overloaded?)"
113+
)
114+
107115
var err error
108116

109117
for backoff.Steps > 0 {
110118
err = f(ctx)
111-
if err == nil {
119+
120+
switch {
121+
case err == nil:
112122
return nil
123+
case strings.Contains(err.Error(), dvcrNoSpaceError):
124+
return fmt.Errorf("%s: %w", dvcrNoSpaceErrMessage, err)
125+
case strings.Contains(err.Error(), dvcrInternalErrorPattern):
126+
return fmt.Errorf("%s: %w", internalDvcrErrMessage, err)
113127
}
114128

115129
if backoff.Steps == 1 {

0 commit comments

Comments
 (0)