• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kubevirt / containerized-data-importer / #6131

22 Jul 2026 12:11PM UTC coverage: 49.763% (-0.05%) from 49.813%
#6131

push

travis-ci

web-flow
Remove pkg/system and refactor qemu execution into pkg/image (#4200)

Signed-off-by: Davide Marro <dmarro@redhat.com>

98 of 116 new or added lines in 2 files covered. (84.48%)

9 existing lines in 1 file now uncovered.

15131 of 30406 relevant lines covered (49.76%)

0.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.89
/pkg/image/exec.go
1
package image
2

3
import (
4
        "bufio"
5
        "bytes"
6
        "context"
7
        "errors"
8
        "fmt"
9
        "net/url"
10
        "os/exec"
11
        "time"
12

13
        "k8s.io/klog/v2"
14
)
15

16
const (
17
        qemuInfoTimeout = 30 * time.Second
18
        qemuImg         = "qemu-img"
19
)
20

21
// scanLinesWithCR splits on both '\r' and '\n'.
22
// This is needed because qemu-img -p outputs progress updates separated by '\r'.
23
func scanLinesWithCR(data []byte, atEOF bool) (advance int, token []byte, err error) {
1✔
24
        if atEOF && len(data) == 0 {
2✔
25
                return 0, nil, nil
1✔
26
        }
1✔
27
        if i := bytes.IndexByte(data, '\r'); i >= 0 {
2✔
28
                return i + 1, data[0:i], nil
1✔
29
        }
1✔
30
        if i := bytes.IndexByte(data, '\n'); i >= 0 {
2✔
31
                return i + 1, data[0:i], nil
1✔
32
        }
1✔
33
        if atEOF {
2✔
34
                return len(data), data, nil
1✔
35
        }
1✔
36
        return 0, nil, nil
1✔
37
}
38

39
type cmdExecError struct {
40
        name   string
41
        stderr string
42
        err    error
43
}
44

45
func (e *cmdExecError) Error() string {
1✔
46
        return fmt.Sprintf("%s execution failed: %s: %s", e.name, e.stderr, e.err)
1✔
47
}
1✔
48

NEW
49
func (e *cmdExecError) Unwrap() error {
×
NEW
50
        return e.err
×
NEW
51
}
×
52

53
// qemuCmd provides a domain-specific API for executing qemu-img (and other)commands
54
type qemuCmd struct {
55
        run    func(ctx context.Context, name string, args ...string) ([]byte, error)
56
        stream func(ctx context.Context, callback func(string), name string, args ...string) error
57
}
58

59
func newQemuCmd() *qemuCmd {
1✔
60
        q := &qemuCmd{}
1✔
61
        q.run = q.defaultRun
1✔
62
        q.stream = q.defaultRunWithStream
1✔
63
        return q
1✔
64
}
1✔
65

66
// Exec runs qemu-img with the given args, discarding stdout
67
func (q *qemuCmd) Exec(args ...string) error {
1✔
68
        _, err := q.run(context.Background(), qemuImg, args...)
1✔
69
        return err
1✔
70
}
1✔
71

72
// Info runs qemu-img info with a timeout and returns stdout (for JSON parsing)
73
func (q *qemuCmd) Info(timeout time.Duration, url *url.URL) ([]byte, error) {
1✔
74
        ctx, cancel := context.WithTimeout(context.Background(), timeout)
1✔
75
        defer cancel()
1✔
76
        return q.run(ctx, qemuImg, "info", "--output=json", url.String())
1✔
77
}
1✔
78

79
// ExecWithProgress runs qemu-img streaming stdout through reportProgress
80
func (q *qemuCmd) ExecWithProgress(args ...string) error {
1✔
81
        return q.stream(context.Background(), reportProgress, qemuImg, args...)
1✔
82
}
1✔
83

84
// ExecRaw runs an arbitrary command (e.g. dd), discarding stdout
85
func (q *qemuCmd) ExecRaw(name string, args ...string) error {
1✔
86
        _, err := q.run(context.Background(), name, args...)
1✔
87
        return err
1✔
88
}
1✔
89

90
func (q *qemuCmd) defaultRun(ctx context.Context, name string, args ...string) ([]byte, error) {
1✔
91
        c := exec.CommandContext(ctx, name, args...)
1✔
92
        output, err := c.Output()
1✔
93
        if err != nil {
2✔
94
                var exitErr *exec.ExitError
1✔
95
                if errors.As(err, &exitErr) {
2✔
96
                        klog.Errorf("%s failed output is:\n%s\n%s", name, string(output), string(exitErr.Stderr))
1✔
97
                        return exitErr.Stderr, &cmdExecError{name: name, stderr: string(exitErr.Stderr), err: err}
1✔
98
                }
1✔
99
                return nil, &cmdExecError{name: name, err: err}
1✔
100
        }
101
        return output, nil
1✔
102
}
103

104
func (q *qemuCmd) defaultRunWithStream(ctx context.Context, callback func(string), name string, args ...string) error {
1✔
105
        c := exec.CommandContext(ctx, name, args...)
1✔
106

1✔
107
        var errBuf bytes.Buffer
1✔
108
        c.Stderr = &errBuf
1✔
109

1✔
110
        stdout, err := c.StdoutPipe()
1✔
111
        if err != nil {
1✔
NEW
112
                return fmt.Errorf("stdout pipe for %s: %w", name, err)
×
NEW
113
        }
×
114

115
        if err := c.Start(); err != nil {
1✔
NEW
116
                return fmt.Errorf("start %s: %w", name, err)
×
NEW
117
        }
×
118

119
        scanner := bufio.NewScanner(stdout)
1✔
120
        scanner.Split(scanLinesWithCR)
1✔
121
        for scanner.Scan() {
2✔
122
                if callback != nil {
2✔
123
                        callback(scanner.Text())
1✔
124
                }
1✔
125
        }
126

127
        if err := scanner.Err(); err != nil {
1✔
NEW
128
                klog.Warningf("%s: error reading stdout: %v", name, err)
×
NEW
129
        }
×
130

131
        err = c.Wait()
1✔
132
        if err != nil {
2✔
133
                klog.Errorf("%s failed, stderr:\n%s", name, errBuf.String())
1✔
134
                return &cmdExecError{name: name, stderr: errBuf.String(), err: err}
1✔
135
        }
1✔
136
        return nil
1✔
137
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc