Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/dependencies/deps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ import (
)

func TestMinimumOSBuildVersion(t *testing.T) {
assert.Equal(t, "160", dependencies.MinimumOSBuildVersion())
assert.Equal(t, "163", dependencies.MinimumOSBuildVersion())
}
2 changes: 1 addition & 1 deletion data/dependencies/osbuild
Original file line number Diff line number Diff line change
@@ -1 +1 @@
160
163
106 changes: 102 additions & 4 deletions pkg/osbuild/monitor.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ func NewStatusScanner(r io.Reader) *StatusScanner {
scanner: scanner,
contextMap: make(map[string]*contextJSON),
stageContextMap: make(map[string]*stageContextJSON),
resultMap: make(map[string]*resultJSON),
metadataMap: make(map[string]json.RawMessage),
}
}

Expand All @@ -88,6 +90,8 @@ type StatusScanner struct {
scanner *bufio.Scanner
contextMap map[string]*contextJSON
stageContextMap map[string]*stageContextJSON
resultMap map[string]*resultJSON
metadataMap map[string]json.RawMessage
}

// Status returns a single status struct from the scanner or nil
Expand Down Expand Up @@ -163,15 +167,102 @@ func (sr *StatusScanner) Status() (*Status, error) {
prog = prog.SubProgress
}

// add result if set
if status.Result.ID != "" {
sr.resultMap[id] = &status.Result
}
if len(status.Metadata) > 0 {
sr.metadataMap[id] = status.Metadata
}

return st, nil
}

func (sr *StatusScanner) Result() (*Result, error) {
// attempt to capture validation errors first
var valRes Result
line := sr.scanner.Bytes()
line = bytes.Trim(line, "\x1e")
if err := json.Unmarshal(line, &valRes); err == nil {
if valRes.Type == "https://osbuild.org/validation-error" {
return &valRes, nil
}
}

result := &Result{
Type: "result",
Log: make(map[string]PipelineResult),
Metadata: make(map[string]PipelineMetadata),
// success is sum of all stages, set to true at first
Success: true,
}

for contextID, stageResult := range sr.resultMap {
if !stageResult.Success {
result.Success = false
}
pipelineName := sr.contextMap[contextID].Pipeline.Name
stageName := sr.contextMap[contextID].Pipeline.Stage.Name
if stageName == "" {
// only stage results are present in osbuild.Result, pipeline results are ignored
continue
}
stageError := ""
if !stageResult.Success {
stageError = stageResult.Output
}
result.Log[pipelineName] = append(result.Log[pipelineName], StageResult{
ID: stageResult.ID,
Type: stageName,
Output: stageResult.Output,
Success: stageResult.Success,
Error: stageError,
})
}

for contextID, rawMD := range sr.metadataMap {
if len(rawMD) == 0 {
continue
}
pipelineName := sr.contextMap[contextID].Pipeline.Name
stageName := sr.contextMap[contextID].Pipeline.Stage.Name

if stageName == "" {
// metadata should only be attached to stage results
continue
}

var stageMD StageMetadata
switch stageName {
case "org.osbuild.rpm":
stageMD = new(RPMStageMetadata)
if err := json.Unmarshal(rawMD, stageMD); err != nil {
return nil, err
}
case "org.osbuild.ostree.commit":
stageMD = new(OSTreeCommitStageMetadata)
if err := json.Unmarshal(rawMD, stageMD); err != nil {
return nil, err
}
default:
stageMD = RawStageMetadata(rawMD)
}

if _, ok := result.Metadata[pipelineName]; !ok {
result.Metadata[pipelineName] = make(map[string]StageMetadata)
}
result.Metadata[pipelineName][stageName] = stageMD
}

return result, nil
}

// statusJSON is a single status entry from the osbuild monitor
type statusJSON struct {
Context contextJSON `json:"context"`
Progress progressJSON `json:"progress"`
// Add "Result" here once
// https://github.com/osbuild/osbuild/pull/1831 is merged
Context contextJSON `json:"context"`
Progress progressJSON `json:"progress"`
Result resultJSON `json:"result"`
Metadata json.RawMessage `json:"metadata"`

Message string `json:"message"`
Timestamp float64 `json:"timestamp"`
Expand Down Expand Up @@ -204,3 +295,10 @@ type progressJSON struct {

SubProgress *progressJSON `json:"progress"`
}

type resultJSON struct {
Name string `json:"name"`
ID string `json:"id"`
Success bool `json:"success"`
Output string `json:"output"`
}
52 changes: 49 additions & 3 deletions pkg/osbuild/monitor_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ func TestScannerDuration(t *testing.T) {
}, st)
}

//go:embed testdata/monitor-fedora-42-raw-v159.seq.json
var osbuildMonitorDuration_fedora []byte
//go:embed testdata/monitor-fedora-42-raw-v197.seq.json
var osbuildMonitor_fedora []byte

func TestScannerPipelineName(t *testing.T) {
scanner := osbuild.NewStatusScanner(bytes.NewBuffer(osbuildMonitorDuration_fedora))
scanner := osbuild.NewStatusScanner(bytes.NewBuffer(osbuildMonitor_fedora))

// the first line is context-less
_, err := scanner.Status()
Expand All @@ -187,6 +187,14 @@ func TestScannerPipelineName(t *testing.T) {
"source org.osbuild.librepo",
"build",
}, slices.Compact(pipelines))

res, err := scanner.Result()
assert.NoError(t, err)
assert.True(t, res.Success)
assert.Contains(t, res.Metadata["build"], "org.osbuild.rpm")
metadata, ok := res.Metadata["build"]["org.osbuild.rpm"].(*osbuild.RPMStageMetadata)
assert.True(t, ok)
assert.Len(t, metadata.Packages, 162)
}

func TestScannerEmpty(t *testing.T) {
Expand All @@ -201,3 +209,41 @@ func TestScannerEmpty(t *testing.T) {
},
}, st)
}

//go:embed testdata/monitor-validation.json
var osbuildMonitorValidation []byte

func TestScannerValidationFailure(t *testing.T) {
scanner := osbuild.NewStatusScanner(bytes.NewBuffer(osbuildMonitorValidation))

st, err := scanner.Status()
assert.NoError(t, err)
assert.Equal(t, &osbuild.Status{
Progress: &osbuild.Progress{
Total: 0,
Done: 0,
},
}, st)

res, err := scanner.Result()
assert.NoError(t, err)
assert.False(t, res.Success)
assert.Equal(t, &osbuild.Result{
Type: "https://osbuild.org/validation-error",
Success: false,
Error: nil,
Log: nil,
Metadata: nil,
Errors: []osbuild.ValidationError{
{
Message: "{'type': 'org.osbuild.files', 'origin': 'org.osbuild.source', 'somekey': 'bad', 'references': [{}] is not valid under any of the given schemas}",
Path: []string{"pipelines", "[0]", "stages", "[0]", "inputs", "packages"},
},
{
Message: "Additional properties are not allowed ('somekey' was unexpected)",
Path: []string{"pipelines", "[0]", "stages", "[0]", "inputs", "packages"},
},
},
Title: "JSON Schema validation failed",
}, res)
}
Loading