osbuild: Add validation error logging

osbuild can return a json object with details about manifest validation
errors. This adds support for saving those, and printing them when the
Write function is called. eg. when using composer-cli compose log UUID

Includes tests for new behavior.
This commit is contained in:
Brian C. Lane 2023-06-06 11:39:13 -07:00 committed by Achilleas Koutsou
parent 8398f27742
commit b57a0d322f
3 changed files with 103 additions and 0 deletions

View file

@ -5,6 +5,7 @@ import (
"fmt"
"io"
"sort"
"strings"
)
type Result struct {
@ -13,6 +14,13 @@ type Result struct {
Error json.RawMessage `json:"error,omitempty"`
Log map[string]PipelineResult `json:"log"`
Metadata map[string]PipelineMetadata `json:"metadata"`
Errors []ValidationError `json:"errors,omitempty"`
Title string `json:"title,omitempty"`
}
type ValidationError struct {
Message string `json:"message"`
Path []string `json:"path"`
}
type PipelineResult []StageResult
@ -116,6 +124,14 @@ func (res *Result) UnmarshalJSON(data []byte) error {
}
func (res *Result) Write(writer io.Writer) error {
// Error may be included, print them first
if res != nil && len(res.Errors) > 0 {
fmt.Fprintf(writer, "Error %s\n", res.Title)
for _, e := range res.Errors {
fmt.Fprintf(writer, "%s: %s\n", strings.Join(e.Path, "."), e.Message)
}
}
if res == nil || res.Log == nil {
fmt.Fprintf(writer, "The compose result is empty.\n")
return nil
@ -155,3 +171,34 @@ func (res *Result) Write(writer io.Writer) error {
return nil
}
// The ValidationError path from osbuild can contain strings or numbers
// json represents all numbers as float64 but since we know they are really
// ints any fractional part is truncated when converting to a string.
func (ve *ValidationError) UnmarshalJSON(data []byte) error {
var raw struct {
Message string
Path []interface{}
}
if err := json.Unmarshal(data, &raw); err != nil {
return err
}
ve.Message = raw.Message
// Convert the path elements to strings
var path []string
for _, p := range raw.Path {
switch v := p.(type) {
// json converts numbers, even 0, to float64 not int
case float64:
path = append(path, fmt.Sprintf("[%0.0f]", v))
case string:
path = append(path, v)
default:
return fmt.Errorf("Unexpected type in ValidationError Path: %#v", v)
}
}
ve.Path = path
return nil
}

View file

@ -6,6 +6,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/osbuild/osbuild-composer/internal/common"
)
@ -203,6 +204,20 @@ func TestUnmarshalV2Failure(t *testing.T) {
}
}
func TestUnmarshalValidationFailure(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
var result Result
err := json.Unmarshal([]byte(validationResultFailure), &result)
assert.NoError(err)
assert.False(result.Success)
assert.Equal(result.Title, "JSON Schema validation failed")
require.Len(result.Errors, 2)
assert.Len(result.Errors[0].Path, 2)
assert.Len(result.Errors[1].Path, 5)
}
func TestWrite(t *testing.T) {
assert := assert.New(t)
result := Result{
@ -335,3 +350,29 @@ func TestWriteEmpty(t *testing.T) {
assert.NoError(result.Write(&b))
assert.Equal("The compose result is empty.\n", b.String())
}
func TestValidationFailWrite(t *testing.T) {
assert := assert.New(t)
result := Result{
Type: "https://osbuild.org/validation-error",
Success: false,
Title: "JSON Schema validation failed",
Errors: []ValidationError{{
Message: "Additional properties are not allowed ('homer' was unexpected)",
Path: []string{"sources", "org.osbuild.curl"},
}, {
Message: "Additional properties are not allowed ('file_contextso' was unexpected)",
Path: []string{"pipelines", "[0]", "stages", "[1]", "options"},
}},
}
var b bytes.Buffer
assert.NoError(result.Write(&b))
expectedOutput := `Error JSON Schema validation failed
sources.org.osbuild.curl: Additional properties are not allowed ('homer' was unexpected)
pipelines.[0].stages.[1].options: Additional properties are not allowed ('file_contextso' was unexpected)
The compose result is empty.
`
assert.Equal(expectedOutput, b.String())
}

View file

@ -7872,3 +7872,18 @@ const fullResultRaw = `
}
}
`
const validationResultFailure = `
{
"type": "https://osbuild.org/validation-error",
"success": false,
"title": "JSON Schema validation failed",
"errors": [{
"message": "Additional properties are not allowed ('homer' was unexpected)",
"path": ["sources", "org.osbuild.curl"]
},{
"message": "Additional properties are not allowed ('file_contextso' was unexpected)",
"path": ["pipelines", 0, "stages", 1, "options"]
}]
}
`