osbuild output should be always JSON-parsable. However, if a user has a weird installation of osbuild, it can return malformed JSON. In this case, it's very hard to debug what happened because the worker doesn't provide any useful information. This commit adds the non-parsable osbuild output to the error so there's at least some clue in the logs what happened. Let me reiterate: In 99% these cases, this shouldn't be needed. It will give us some hints in these 1% cases though. You may want to ask if using json.Decoder isn't better because it doesn't buffer the input. However, it does buffer. Firstly, json.Decoder.Decode() buffers the whole JSON value (it contains kinda cool state machine to do it) and in the second run, it parses the buffer. Therefore, the only thing that this commit actually does is that it moves the buffer out of json.Decoder. Signed-off-by: Ondřej Budai <ondrej@budai.cz>
68 lines
1.8 KiB
Go
68 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"os/exec"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/distro"
|
|
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
|
)
|
|
|
|
// Run an instance of osbuild, returning a parsed osbuild.Result.
|
|
//
|
|
// Note that osbuild returns non-zero when the pipeline fails. This function
|
|
// does not return an error in this case. Instead, the failure is communicated
|
|
// with its corresponding logs through osbuild.Result.
|
|
func RunOSBuild(manifest distro.Manifest, store, outputDirectory string, errorWriter io.Writer) (*osbuild.Result, error) {
|
|
cmd := exec.Command(
|
|
"osbuild",
|
|
"--store", store,
|
|
"--output-directory", outputDirectory,
|
|
"--json", "-",
|
|
)
|
|
cmd.Stderr = errorWriter
|
|
|
|
stdin, err := cmd.StdinPipe()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error setting up stdin for osbuild: %v", err)
|
|
}
|
|
|
|
var stdoutBuffer bytes.Buffer
|
|
cmd.Stdout = &stdoutBuffer
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error starting osbuild: %v", err)
|
|
}
|
|
|
|
err = json.NewEncoder(stdin).Encode(manifest)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error encoding osbuild pipeline: %v", err)
|
|
}
|
|
|
|
err = stdin.Close()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("error closing osbuild's stdin: %v", err)
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
|
|
// try to decode the output even though the job could have failed
|
|
var result osbuild.Result
|
|
decodeErr := json.Unmarshal(stdoutBuffer.Bytes(), &result)
|
|
if decodeErr != nil {
|
|
return nil, fmt.Errorf("error decoding osbuild output: %v\nthe raw output:\n%s", decodeErr, stdoutBuffer.String())
|
|
}
|
|
|
|
if err != nil {
|
|
// ignore ExitError if output could be decoded correctly
|
|
if _, isExitError := err.(*exec.ExitError); !isExitError {
|
|
return nil, fmt.Errorf("running osbuild failed: %v", err)
|
|
}
|
|
}
|
|
|
|
return &result, nil
|
|
}
|