From 5803abfeee9881a96899dce993f86fec60e2d913 Mon Sep 17 00:00:00 2001 From: Tom Gundersen Date: Tue, 25 Aug 2020 23:53:58 +0100 Subject: [PATCH] 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 --- internal/osbuild/result.go | 73 +++++++++++++++++++++------ internal/osbuild/result_test.go | 88 ++++++++++++++++++++++++++++++--- internal/osbuild/rpm_stage.go | 17 +++++++ 3 files changed, 154 insertions(+), 24 deletions(-) diff --git a/internal/osbuild/result.go b/internal/osbuild/result.go index dee4b8c51..fe917983c 100644 --- a/internal/osbuild/result.go +++ b/internal/osbuild/result.go @@ -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 { diff --git a/internal/osbuild/result_test.go b/internal/osbuild/result_test.go index 9ffba32ef..98fed411b 100644 --- a/internal/osbuild/result_test.go +++ b/internal/osbuild/result_test.go @@ -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" } diff --git a/internal/osbuild/rpm_stage.go b/internal/osbuild/rpm_stage.go index 71cbcd20f..b4979cf4d 100644 --- a/internal/osbuild/rpm_stage.go +++ b/internal/osbuild/rpm_stage.go @@ -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() {}