Skip to content

Commit 7a37240

Browse files
committed
write metadata when test image provisioning fails
Put it in the provision-log directory as JSON. Currently, the only captured value is the exit code. This allows some machine-readable distinction by an external observer about why exactly the test image provisioning failed.
1 parent 87aadd4 commit 7a37240

File tree

4 files changed

+45
-14
lines changed

4 files changed

+45
-14
lines changed

cmd/exec.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package cmd
33
import (
44
"bytes"
55
"context"
6-
"io/ioutil"
6+
"encoding/json"
7+
"errors"
78
"os"
89
"os/exec"
910
"path/filepath"
@@ -13,15 +14,22 @@ import (
1314
"golang.org/x/sys/unix"
1415
)
1516

17+
type execResultMeta struct {
18+
ExitCode int `json:"exit_code"`
19+
}
20+
1621
// cmdStderrTerm runs a Cmd, collecting stderr and terminating gracefully
17-
func cmdStderrTerm(ctx context.Context, logger log.FieldLogger, stderrPath string, cmd *exec.Cmd) error {
22+
func cmdStderrTerm(ctx context.Context, logger log.FieldLogger, stderrPath string, metaPath string, cmd *exec.Cmd) error {
1823
var out bytes.Buffer
24+
var meta execResultMeta
1925
cmd.Stderr = &out
2026

2127
err := cmdRunTerm(ctx, logger, cmd)
2228

23-
if exitErr, ok := err.(*exec.ExitError); ok {
29+
var exitErr *exec.ExitError
30+
if errors.As(err, &exitErr) {
2431
exitErr.Stderr = out.Bytes()
32+
meta.ExitCode = exitErr.ExitCode()
2533
}
2634

2735
if mkdirErr := os.MkdirAll(filepath.Dir(stderrPath), 0755); mkdirErr != nil {
@@ -31,13 +39,35 @@ func cmdStderrTerm(ctx context.Context, logger log.FieldLogger, stderrPath strin
3139
return mkdirErr
3240
}
3341

34-
if stderrWriteErr := ioutil.WriteFile(stderrPath, out.Bytes(), 0644); stderrWriteErr != nil {
42+
if stderrWriteErr := os.WriteFile(stderrPath, out.Bytes(), 0644); stderrWriteErr != nil {
3543
if err != nil {
3644
logger.Errorf("Failed to write stderr; suppressing original error: %v\n", err)
3745
}
3846
return stderrWriteErr
3947
}
4048

49+
if metaPath != "" {
50+
if mkdirErr := os.MkdirAll(filepath.Dir(metaPath), 0755); mkdirErr != nil {
51+
if err != nil {
52+
logger.Errorf("Failed to create directory for metadata; suppressing original error: %v\n", err)
53+
}
54+
return mkdirErr
55+
}
56+
metaBytes, jsonErr := json.Marshal(meta)
57+
if jsonErr != nil {
58+
if err != nil {
59+
logger.Errorf("Failed to marshal meta; suppressing original error: %v\n", err)
60+
}
61+
return jsonErr
62+
}
63+
if metaWriteErr := os.WriteFile(metaPath, metaBytes, 0644); metaWriteErr != nil {
64+
if err != nil {
65+
logger.Errorf("Failed to write meta; suppressing original error: %v\n", err)
66+
}
67+
return metaWriteErr
68+
}
69+
}
70+
4171
return err
4272
}
4373

cmd/network.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func addNetwork(ctx context.Context, outDir string, networkName string, network
4646
stderrPath := filepath.Join(outDir, "network-log", fmt.Sprintf("network_add_%s.log", networkName))
4747

4848
log.Debugf("EXECUTING: %s", argv)
49-
err := cmdStderrTerm(ctx, logger, stderrPath, exec.Command(argv[0], argv[1:]...))
49+
err := cmdStderrTerm(ctx, logger, stderrPath, "", exec.Command(argv[0], argv[1:]...))
5050
if err != nil {
5151
log.WithError(err).Warnf("failed to create test network %s", networkName)
5252
return err
@@ -67,7 +67,7 @@ func removeNetwork(outDir string, networkName string) error {
6767
argv := []string{"virter", "network", "rm", networkName}
6868
stderrPath := filepath.Join(outDir, "network-log", fmt.Sprintf("network_rm_%s.log", networkName))
6969
log.Debugf("EXECUTING: %s", argv)
70-
err := cmdStderrTerm(ctx, logger, stderrPath, exec.Command(argv[0], argv[1:]...))
70+
err := cmdStderrTerm(ctx, logger, stderrPath, "", exec.Command(argv[0], argv[1:]...))
7171
if err != nil {
7272
logger.WithError(err).Warnf("failed to remove test network %s", networkName)
7373
return err

cmd/test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ func copyDir(logger log.FieldLogger, vm vmInstance, logDir string, srcDir string
261261
logger.Debugf("EXECUTING VIRTER COPY: %s", args)
262262
cmd := exec.Command(args[0], args[1:]...)
263263
cmd.Env = virterEnv(vm.networkNames[0])
264-
return cmdStderrTerm(ctx, logger, stderrPath, cmd)
264+
return cmdStderrTerm(ctx, logger, stderrPath, "", cmd)
265265
}
266266

267267
func getArtifactsUrl(outdir string) string {

cmd/vm.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func pullImage(ctx context.Context, suiteRun *testSuiteRun, image string, templ
7878

7979
log.Debugf("EXECUTING: %s", argv)
8080
start := time.Now()
81-
err := cmdStderrTerm(ctx, logger, errPath, cmd)
81+
err := cmdStderrTerm(ctx, logger, errPath, "", cmd)
8282
log.Debugf("EXECUTIONTIME: Pull image %s: %v", image, time.Since(start))
8383

8484
return err
@@ -99,7 +99,7 @@ func provisionImage(ctx context.Context, suiteRun *testSuiteRun, nr int, v *vm,
9999
log.Debugf("EXECUTING: %s", argv)
100100
// this command is idempotent, so even if it does nothing, it returns zero
101101
cmd := exec.Command(argv[0], argv[1:]...)
102-
if err := cmdStderrTerm(ctx, logger, rmStderrPath, cmd); err != nil {
102+
if err := cmdStderrTerm(ctx, logger, rmStderrPath, "", cmd); err != nil {
103103
return err
104104
}
105105

@@ -135,6 +135,7 @@ func provisionImage(ctx context.Context, suiteRun *testSuiteRun, nr int, v *vm,
135135
argv = append(argv, v.BaseImage, newImageName)
136136

137137
stderrPath := filepath.Join(outDir, fmt.Sprintf("%s-provision.log", newImageName))
138+
metaPath := filepath.Join(outDir, fmt.Sprintf("%s-meta.json", newImageName))
138139

139140
cmd = exec.Command(argv[0], argv[1:]...)
140141
cmd.Env = virterEnv(networkName)
@@ -144,7 +145,7 @@ func provisionImage(ctx context.Context, suiteRun *testSuiteRun, nr int, v *vm,
144145

145146
log.Debugf("EXECUTING: %s", argv)
146147
start := time.Now()
147-
err := cmdStderrTerm(provisionCtx, logger, stderrPath, cmd)
148+
err := cmdStderrTerm(provisionCtx, logger, stderrPath, metaPath, cmd)
148149
log.Debugf("EXECUTIONTIME: Provisioning image %s: %v", newImageName, time.Since(start))
149150

150151
if ctx.Err() != nil {
@@ -173,7 +174,7 @@ func removeImages(outDir string, vmSpec *vmSpecification) {
173174
stderrPath := filepath.Join(provisionOutDir, fmt.Sprintf("image_rm_%s.log", newImageName))
174175
log.Debugf("EXECUTING: %s", argv)
175176
cmd := exec.Command(argv[0], argv[1:]...)
176-
if err := cmdStderrTerm(ctx, log.StandardLogger(), stderrPath, cmd); err != nil {
177+
if err := cmdStderrTerm(ctx, log.StandardLogger(), stderrPath, "", cmd); err != nil {
177178
log.Errorf("ERROR: Could not remove image %s %v", newImageName, err)
178179
dumpStderr(log.StandardLogger(), err)
179180
// do not return, keep going...
@@ -213,7 +214,7 @@ func runVM(ctx context.Context, logger *log.Logger, run *testRun, vm vmInstance)
213214
// this command is idempotent, so even if it does nothing, it returns zero
214215
cmd := exec.Command(argv[0], argv[1:]...)
215216
cmd.Env = virterEnv(vm.networkNames[0])
216-
if err := cmdStderrTerm(ctx, logger, rmStderrPath, cmd); err != nil {
217+
if err := cmdStderrTerm(ctx, logger, rmStderrPath, "", cmd); err != nil {
217218
return err
218219
}
219220

@@ -245,7 +246,7 @@ func runVM(ctx context.Context, logger *log.Logger, run *testRun, vm vmInstance)
245246
logger.Debugf("EXECUTING: %s", argv)
246247
cmd = exec.Command(argv[0], argv[1:]...)
247248
cmd.Env = virterEnv(vm.networkNames[0])
248-
return cmdStderrTerm(ctx, logger, stderrPath, cmd)
249+
return cmdStderrTerm(ctx, logger, stderrPath, "", cmd)
249250
}
250251

251252
func shutdownVMs(logger *log.Logger, outDir string, res *testResult, suiteRun *testSuiteRun, testnodes ...vmInstance) {
@@ -269,7 +270,7 @@ func shutdownVMs(logger *log.Logger, outDir string, res *testResult, suiteRun *t
269270
logger.Debugf("EXECUTING: %s", argv)
270271
cmd := exec.Command(argv[0], argv[1:]...)
271272
cmd.Env = virterEnv(vm.networkNames[0])
272-
if err := cmdStderrTerm(ctx, logger, stderrPath, cmd); err != nil {
273+
if err := cmdStderrTerm(ctx, logger, stderrPath, "", cmd); err != nil {
273274
logger.Errorf("ERROR: Could not stop VM %s: %v", vmName, err)
274275
dumpStderr(logger, err)
275276
// do not return, keep going...

0 commit comments

Comments
 (0)