diff --git a/pkg/osbuild/osbuild-exec.go b/pkg/osbuild/osbuild-exec.go index e49929e038..225178b7bc 100644 --- a/pkg/osbuild/osbuild-exec.go +++ b/pkg/osbuild/osbuild-exec.go @@ -144,3 +144,16 @@ func OSBuildVersion() (string, error) { version = strings.TrimSpace(version) return version, nil } + +// OSBuildInspect converts a manifest to an inspected manifest. +func OSBuildInspect(manifest []byte) ([]byte, error) { + cmd := exec.Command("osbuild", "--inspect") + cmd.Stdin = bytes.NewBuffer(manifest) + + out, err := cmd.Output() + if err != nil { + return nil, err + } + + return out, nil +} diff --git a/pkg/osbuild/osbuild.go b/pkg/osbuild/osbuild.go index 9950dcab0a..ac7aa7bb21 100644 --- a/pkg/osbuild/osbuild.go +++ b/pkg/osbuild/osbuild.go @@ -2,6 +2,11 @@ // OSBuild (schema v2) types. package osbuild +import ( + "encoding/json" + "fmt" +) + const ( // should be "^\\/(?!\\.\\.)((?!\\/\\.\\.\\/).)+$" but Go doesn't support lookaheads // therefore we have to instead check for the invalid cases, which is much simpler @@ -51,3 +56,31 @@ func (p *Pipeline) AddStages(stages ...*Stage) { p.AddStage(stage) } } + +// Take some bytes and deserialize them into a Manifest; mostly used to take +// an inspected manifest +func NewManifestFromBytes(data []byte) (*Manifest, error) { + manifest := &Manifest{} + + if err := json.Unmarshal(data, &manifest); err != nil { + return nil, err + } + + return manifest, nil +} + +// GetID gets the pipeline identifiers for an *inspected* manifest. These are +// not available for non-inspected manifests and will return an error there. +func (p *Pipeline) GetID() (string, error) { + if len(p.Stages) == 0 { + return "", fmt.Errorf("no stages in manifest") + } + + lastStage := p.Stages[len(p.Stages)-1] + + if len(lastStage.ID) == 0 { + return "", fmt.Errorf("un-inspected manifest, identifiers are not available") + } + + return lastStage.ID, nil +} diff --git a/pkg/osbuild/osbuild_test.go b/pkg/osbuild/osbuild_test.go index 817532023f..32a2ee1fb0 100644 --- a/pkg/osbuild/osbuild_test.go +++ b/pkg/osbuild/osbuild_test.go @@ -26,3 +26,35 @@ func TestPipeline_AddStage(t *testing.T) { assert.Equal(t, expectedPipeline, actualPipeline) assert.Equal(t, 1, len(actualPipeline.Stages)) } + +var fakeOsbuildManifestWithIdentifiers = []byte(`{ + "version": "2", + "pipelines": [ + { + "name": "build", + "stages": [ + { + "id": "1234", + "type": "org.osbuild.rpm" + }, + { + "id": "5678", + "type": "org.osbuild.mkdir" + } + ] + } + ] +}`) + +func TestManifestFromBytes(t *testing.T) { + manifest, err := NewManifestFromBytes(fakeOsbuildManifestWithIdentifiers) + assert.NoError(t, err) + + assert.Equal(t, manifest.Pipelines[0].Stages[0].ID, "1234") + assert.Equal(t, manifest.Pipelines[0].Stages[1].ID, "5678") + + pID, err := manifest.Pipelines[0].GetID() + assert.NoError(t, err) + + assert.Equal(t, pID, "5678") +} diff --git a/pkg/osbuild/stage.go b/pkg/osbuild/stage.go index d192e5ff55..0669117c6d 100644 --- a/pkg/osbuild/stage.go +++ b/pkg/osbuild/stage.go @@ -5,6 +5,9 @@ type Stage struct { // Well-known name in reverse domain-name notation, uniquely identifying // the stage type. Type string `json:"type"` + + ID string `json:"id,omitempty"` + // Stage-type specific options fully determining the operations of the Inputs Inputs `json:"inputs,omitempty"`