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"
)
type assembler struct {
type rawAssemblerResult struct {
Name string `json:"name"`
Options json.RawMessage `json:"options"`
Success bool `json:"success"`
Output string `json:"output"`
}
type stage struct {
Name string `json:"name"`
Options json.RawMessage `json:"options"`
Success bool `json:"success"`
Output string `json:"output"`
type StageResult struct {
Name string `json:"name"`
Options json.RawMessage `json:"options"`
Success bool `json:"success"`
Output string `json:"output"`
Metadata StageMetadata `json:"metadata"`
}
type build struct {
Stages []stage `json:"stages"`
TreeID string `json:"tree_id"`
Success bool `json:"success"`
// StageMetadata specify the metadata of a given stage-type.
type StageMetadata interface {
isStageMetadata()
}
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 {
TreeID string `json:"tree_id"`
OutputID string `json:"output_id"`
Build *build `json:"build"`
Stages []stage `json:"stages"`
Assembler *assembler `json:"assembler"`
Success bool `json:"success"`
TreeID string `json:"tree_id"`
OutputID string `json:"output_id"`
Build *buildResult `json:"build"`
Stages []StageResult `json:"stages"`
Assembler *rawAssemblerResult `json:"assembler"`
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 {

View file

@ -2,29 +2,101 @@ package osbuild
import (
"bytes"
"encoding/json"
"testing"
"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) {
const testOptions = `{"msg": "test"}`
testStage := stage{
Name: "testStage",
dnfStage := StageResult{
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),
Success: true,
Output: "Finished",
}
testBuild := build{
Stages: []stage{testStage},
testBuild := buildResult{
Stages: []StageResult{testStage},
TreeID: "treeID",
Success: true,
}
testAssembler := assembler{
testAssembler := rawAssemblerResult{
Name: "testAssembler",
Options: []byte(testOptions),
Success: true,
@ -35,7 +107,7 @@ func TestWriteFull(t *testing.T) {
TreeID: "TreeID",
OutputID: "OutputID",
Build: &testBuild,
Stages: []stage{testStage},
Stages: []StageResult{dnfStage},
Assembler: &testAssembler,
Success: true,
}
@ -44,7 +116,7 @@ func TestWriteFull(t *testing.T) {
assert.NoError(t, testComposeResult.Write(&b))
expectedMessage :=
`Build pipeline:
Stage testStage
Stage org.osbuild.test
{
"msg": "test"
}
@ -52,7 +124,7 @@ Stage testStage
Output:
Finished
Stages:
Stage: testStage
Stage: org.osbuild.rpm
{
"msg": "test"
}

View file

@ -28,3 +28,20 @@ func NewRPMStage(options *RPMStageOptions) *Stage {
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() {}