osbuild/result: parse the metadata from the rpm stage

osbuild support returning metadata about each of the stages/assembler
runs. Parse the results from the rpm stage, which contains the header
fields from the installed RPMs, in particular the MD5 sum of the RPMs in
question. This information is needed to be passed as metadata to koji
when uploading images.

Signed-off-by: Tom Gundersen <teg@jklm.no>
This commit is contained in:
Tom Gundersen 2020-08-25 23:53:58 +01:00 committed by Ondřej Budai
parent ac5f69e757
commit 5803abfeee
3 changed files with 154 additions and 24 deletions

View file

@ -6,33 +6,74 @@ import (
"io" "io"
) )
type assembler struct { type rawAssemblerResult struct {
Name string `json:"name"` Name string `json:"name"`
Options json.RawMessage `json:"options"` Options json.RawMessage `json:"options"`
Success bool `json:"success"` Success bool `json:"success"`
Output string `json:"output"` Output string `json:"output"`
} }
type stage struct { type StageResult struct {
Name string `json:"name"` Name string `json:"name"`
Options json.RawMessage `json:"options"` Options json.RawMessage `json:"options"`
Success bool `json:"success"` Success bool `json:"success"`
Output string `json:"output"` Output string `json:"output"`
Metadata StageMetadata `json:"metadata"`
} }
type build struct { // StageMetadata specify the metadata of a given stage-type.
Stages []stage `json:"stages"` type StageMetadata interface {
TreeID string `json:"tree_id"` isStageMetadata()
Success bool `json:"success"` }
type rawStageResult struct {
Name string `json:"name"`
Options json.RawMessage `json:"options"`
Success bool `json:"success"`
Output string `json:"output"`
Metadata json.RawMessage `json:"metadata"`
}
type buildResult struct {
Stages []StageResult `json:"stages"`
TreeID string `json:"tree_id"`
Success bool `json:"success"`
} }
type Result struct { type Result struct {
TreeID string `json:"tree_id"` TreeID string `json:"tree_id"`
OutputID string `json:"output_id"` OutputID string `json:"output_id"`
Build *build `json:"build"` Build *buildResult `json:"build"`
Stages []stage `json:"stages"` Stages []StageResult `json:"stages"`
Assembler *assembler `json:"assembler"` Assembler *rawAssemblerResult `json:"assembler"`
Success bool `json:"success"` Success bool `json:"success"`
}
func (result *StageResult) UnmarshalJSON(data []byte) error {
var rawStageResult rawStageResult
err := json.Unmarshal(data, &rawStageResult)
if err != nil {
return err
}
var metadata StageMetadata
switch rawStageResult.Name {
case "org.osbuild.rpm":
metadata = new(RPMStageMetadata)
err = json.Unmarshal(rawStageResult.Metadata, metadata)
if err != nil {
return err
}
default:
metadata = nil
}
result.Name = rawStageResult.Name
result.Options = rawStageResult.Options
result.Success = rawStageResult.Success
result.Output = rawStageResult.Output
result.Metadata = metadata
return nil
} }
func (cr *Result) Write(writer io.Writer) error { func (cr *Result) Write(writer io.Writer) error {

View file

@ -2,29 +2,101 @@ package osbuild
import ( import (
"bytes" "bytes"
"encoding/json"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
) )
func TestUnmarshall(t *testing.T) {
resultRaw := `{
"success": true,
"build": {
"success": true,
"stages": [
{
"name": "org.osbuild.rpm",
"id": "9eb0a6f6fd6e2995e107f5bcc6aa3b19643b02ec133bdc8a8ac614860b1bbf2d",
"success": true,
"output": "Building...",
"metadata": {
"packages": [
{
"name": "libgcc",
"version": "10.0.1",
"release": "0.11.fc32",
"epoch": null,
"arch": "x86_64",
"sigmd5": "84fc907a5047aeebaf8da1642925a417"
},
{
"name": "whois-nls",
"version": "5.5.6",
"release": "1.fc32",
"epoch": null,
"arch": "noarch",
"sigmd5": "f868cd02046630c8ce3a9c48820e2437"
}
]
}
}
]
}
}`
var result Result
err := json.Unmarshal([]byte(resultRaw), &result)
assert.NoError(t, err)
assert.Equal(t, result.Build.Stages[0].Name, "org.osbuild.rpm")
metadata, ok := result.Build.Stages[0].Metadata.(*RPMStageMetadata)
assert.True(t, ok)
package1 := metadata.Packages[0]
assert.Equal(t, package1.Name, "libgcc")
assert.Nil(t, package1.Epoch)
assert.Equal(t, package1.Version, "10.0.1")
assert.Equal(t, package1.Release, "0.11.fc32")
assert.Equal(t, package1.Arch, "x86_64")
assert.Equal(t, package1.SigMD5, "84fc907a5047aeebaf8da1642925a417")
}
func TestWriteFull(t *testing.T) { func TestWriteFull(t *testing.T) {
const testOptions = `{"msg": "test"}` const testOptions = `{"msg": "test"}`
testStage := stage{ dnfStage := StageResult{
Name: "testStage", Name: "org.osbuild.rpm",
Options: []byte(testOptions),
Success: true,
Output: "Finished",
Metadata: RPMStageMetadata{
Packages: []RPMPackageMetadata{
{
Name: "foobar",
Epoch: nil,
Version: "1",
Release: "1",
Arch: "noarch",
SigMD5: "deadbeef",
},
},
},
}
testStage := StageResult{
Name: "org.osbuild.test",
Options: []byte(testOptions), Options: []byte(testOptions),
Success: true, Success: true,
Output: "Finished", Output: "Finished",
} }
testBuild := build{ testBuild := buildResult{
Stages: []stage{testStage}, Stages: []StageResult{testStage},
TreeID: "treeID", TreeID: "treeID",
Success: true, Success: true,
} }
testAssembler := assembler{ testAssembler := rawAssemblerResult{
Name: "testAssembler", Name: "testAssembler",
Options: []byte(testOptions), Options: []byte(testOptions),
Success: true, Success: true,
@ -35,7 +107,7 @@ func TestWriteFull(t *testing.T) {
TreeID: "TreeID", TreeID: "TreeID",
OutputID: "OutputID", OutputID: "OutputID",
Build: &testBuild, Build: &testBuild,
Stages: []stage{testStage}, Stages: []StageResult{dnfStage},
Assembler: &testAssembler, Assembler: &testAssembler,
Success: true, Success: true,
} }
@ -44,7 +116,7 @@ func TestWriteFull(t *testing.T) {
assert.NoError(t, testComposeResult.Write(&b)) assert.NoError(t, testComposeResult.Write(&b))
expectedMessage := expectedMessage :=
`Build pipeline: `Build pipeline:
Stage testStage Stage org.osbuild.test
{ {
"msg": "test" "msg": "test"
} }
@ -52,7 +124,7 @@ Stage testStage
Output: Output:
Finished Finished
Stages: Stages:
Stage: testStage Stage: org.osbuild.rpm
{ {
"msg": "test" "msg": "test"
} }

View file

@ -28,3 +28,20 @@ func NewRPMStage(options *RPMStageOptions) *Stage {
Options: options, Options: options,
} }
} }
// RPMStageMetadata gives the set of packages installed by the RPM stage
type RPMStageMetadata struct {
Packages []RPMPackageMetadata `json:"packages"`
}
// RPMPackageMetadata contains the metadata extracted from one RPM header
type RPMPackageMetadata struct {
Name string `json:"name"`
Version string `json:"version"`
Release string `json:"release"`
Epoch *string `json:"epoch"`
Arch string `json:"arch"`
SigMD5 string `json:"sigmd5"`
}
func (RPMStageMetadata) isStageMetadata() {}