|
Packit Service |
15f37d |
package osbuild1
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
import (
|
|
Packit Service |
15f37d |
"encoding/json"
|
|
Packit Service |
15f37d |
"fmt"
|
|
Packit Service |
15f37d |
"io"
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
"github.com/osbuild/osbuild-composer/internal/osbuild2"
|
|
Packit Service |
15f37d |
)
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
type rawAssemblerResult struct {
|
|
Packit Service |
15f37d |
Name string `json:"name"`
|
|
Packit Service |
15f37d |
Options json.RawMessage `json:"options"`
|
|
Packit Service |
15f37d |
Success bool `json:"success"`
|
|
Packit Service |
15f37d |
Output string `json:"output"`
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
type StageResult struct {
|
|
Packit Service |
15f37d |
Name string `json:"name"`
|
|
Packit Service |
15f37d |
Options json.RawMessage `json:"options"`
|
|
Packit Service |
15f37d |
Success bool `json:"success"`
|
|
Packit Service |
15f37d |
Output string `json:"output"`
|
|
Packit Service |
15f37d |
Metadata StageMetadata `json:"metadata"`
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
// StageMetadata specify the metadata of a given stage-type.
|
|
Packit Service |
15f37d |
type StageMetadata interface {
|
|
Packit Service |
15f37d |
isStageMetadata()
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
type rawStageResult struct {
|
|
Packit Service |
15f37d |
Name string `json:"name"`
|
|
Packit Service |
15f37d |
Options json.RawMessage `json:"options"`
|
|
Packit Service |
15f37d |
Success bool `json:"success"`
|
|
Packit Service |
15f37d |
Output string `json:"output"`
|
|
Packit Service |
15f37d |
Metadata json.RawMessage `json:"metadata"`
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
type buildResult struct {
|
|
Packit Service |
15f37d |
Stages []StageResult `json:"stages"`
|
|
Packit Service |
15f37d |
TreeID string `json:"tree_id"`
|
|
Packit Service |
15f37d |
Success bool `json:"success"`
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
type Result struct {
|
|
Packit Service |
15f37d |
TreeID string `json:"tree_id"`
|
|
Packit Service |
15f37d |
OutputID string `json:"output_id"`
|
|
Packit Service |
15f37d |
Build *buildResult `json:"build"`
|
|
Packit Service |
15f37d |
Stages []StageResult `json:"stages"`
|
|
Packit Service |
15f37d |
Assembler *rawAssemblerResult `json:"assembler"`
|
|
Packit Service |
15f37d |
Success bool `json:"success"`
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
func (result *StageResult) UnmarshalJSON(data []byte) error {
|
|
Packit Service |
15f37d |
var rawStageResult rawStageResult
|
|
Packit Service |
15f37d |
err := json.Unmarshal(data, &rawStageResult)
|
|
Packit Service |
15f37d |
if err != nil {
|
|
Packit Service |
15f37d |
return err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
var metadata StageMetadata
|
|
Packit Service |
15f37d |
switch rawStageResult.Name {
|
|
Packit Service |
15f37d |
case "org.osbuild.rpm":
|
|
Packit Service |
15f37d |
metadata = new(RPMStageMetadata)
|
|
Packit Service |
15f37d |
err = json.Unmarshal(rawStageResult.Metadata, metadata)
|
|
Packit Service |
15f37d |
if err != nil {
|
|
Packit Service |
15f37d |
return err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
default:
|
|
Packit Service |
15f37d |
metadata = nil
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
result.Name = rawStageResult.Name
|
|
Packit Service |
15f37d |
result.Options = rawStageResult.Options
|
|
Packit Service |
15f37d |
result.Success = rawStageResult.Success
|
|
Packit Service |
15f37d |
result.Output = rawStageResult.Output
|
|
Packit Service |
15f37d |
result.Metadata = metadata
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
return nil
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
func (cr *Result) Write(writer io.Writer) error {
|
|
Packit Service |
15f37d |
if cr.Build == nil && len(cr.Stages) == 0 && cr.Assembler == nil {
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "The compose result is empty.\n")
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
if cr.Build != nil {
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "Build pipeline:\n")
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
for _, stage := range cr.Build.Stages {
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "Stage %s\n", stage.Name)
|
|
Packit Service |
15f37d |
enc := json.NewEncoder(writer)
|
|
Packit Service |
15f37d |
enc.SetIndent("", " ")
|
|
Packit Service |
15f37d |
err := enc.Encode(stage.Options)
|
|
Packit Service |
15f37d |
if err != nil {
|
|
Packit Service |
15f37d |
return err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "\nOutput:\n%s\n", stage.Output)
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
if len(cr.Stages) > 0 {
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "Stages:\n")
|
|
Packit Service |
15f37d |
for _, stage := range cr.Stages {
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "Stage: %s\n", stage.Name)
|
|
Packit Service |
15f37d |
enc := json.NewEncoder(writer)
|
|
Packit Service |
15f37d |
enc.SetIndent("", " ")
|
|
Packit Service |
15f37d |
err := enc.Encode(stage.Options)
|
|
Packit Service |
15f37d |
if err != nil {
|
|
Packit Service |
15f37d |
return err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "\nOutput:\n%s\n", stage.Output)
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
if cr.Assembler != nil {
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "Assembler %s:\n", cr.Assembler.Name)
|
|
Packit Service |
15f37d |
enc := json.NewEncoder(writer)
|
|
Packit Service |
15f37d |
enc.SetIndent("", " ")
|
|
Packit Service |
15f37d |
err := enc.Encode(cr.Assembler.Options)
|
|
Packit Service |
15f37d |
if err != nil {
|
|
Packit Service |
15f37d |
return err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
fmt.Fprintf(writer, "\nOutput:\n%s\n", cr.Assembler.Output)
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
return nil
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
217d88 |
// isV2Result returns true if data contains a json-encoded osbuild result
|
|
Packit Service |
217d88 |
// in version 2 schema.
|
|
Packit Service |
217d88 |
//
|
|
Packit Service |
217d88 |
// It detects the schema version by checking if the decoded json contains
|
|
Packit Service |
217d88 |
// a "type" field at the top-level.
|
|
Packit Service |
217d88 |
//
|
|
Packit Service |
217d88 |
// error is non-nil when data isn't a json-encoded object.
|
|
Packit Service |
217d88 |
func isV2Result(data []byte) (bool, error) {
|
|
Packit Service |
217d88 |
var v2ResultStub struct {
|
|
Packit Service |
217d88 |
Type string `json:"type"`
|
|
Packit Service |
217d88 |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
217d88 |
err := json.Unmarshal(data, &v2ResultStub)
|
|
Packit Service |
217d88 |
if err != nil {
|
|
Packit Service |
217d88 |
return false, err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
217d88 |
return v2ResultStub.Type != "", nil
|
|
Packit Service |
217d88 |
}
|
|
Packit Service |
217d88 |
|
|
Packit Service |
217d88 |
// UnmarshalJSON decodes json-encoded data into a Result struct.
|
|
Packit Service |
217d88 |
//
|
|
Packit Service |
217d88 |
// Note that this function is smart and if a result from manifest v2 is given,
|
|
Packit Service |
217d88 |
// it detects it and converts it to a result like it would be returned for
|
|
Packit Service |
217d88 |
// manifest v1. This conversion is always lossy.
|
|
Packit Service |
217d88 |
//
|
|
Packit Service |
217d88 |
// TODO: We might want to get rid of the smart behaviour and make this method
|
|
Packit Service |
217d88 |
// dumb again.
|
|
Packit Service |
217d88 |
func (cr *Result) UnmarshalJSON(data []byte) error {
|
|
Packit Service |
217d88 |
// detect if the input is v2 result
|
|
Packit Service |
217d88 |
v2Result, err := isV2Result(data)
|
|
Packit Service |
217d88 |
if err != nil {
|
|
Packit Service |
217d88 |
return err
|
|
Packit Service |
217d88 |
}
|
|
Packit Service |
217d88 |
if v2Result {
|
|
Packit Service |
217d88 |
// do the best-effort conversion from v2
|
|
Packit Service |
217d88 |
var crv2 osbuild2.Result
|
|
Packit Service |
217d88 |
|
|
Packit Service |
217d88 |
// NOTE: Using plain (non-strict) Unmarshal here. The format of the new
|
|
Packit Service |
217d88 |
// osbuild output schema is not yet fixed and is likely to change, so
|
|
Packit Service |
217d88 |
// disallowing unknown fields will likely cause failures in the near future.
|
|
Packit Service |
217d88 |
if err := json.Unmarshal(data, &crv2); err != nil {
|
|
Packit Service |
217d88 |
return err
|
|
Packit Service |
217d88 |
}
|
|
Packit Service |
217d88 |
cr.fromV2(crv2)
|
|
Packit Service |
217d88 |
return nil
|
|
Packit Service |
217d88 |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
217d88 |
// otherwise, unmarshal using a type alias to prevent recursive calls
|
|
Packit Service |
217d88 |
// of this method.
|
|
Packit Service |
217d88 |
type resultAlias Result
|
|
Packit Service |
217d88 |
var crv1 resultAlias
|
|
Packit Service |
217d88 |
err = json.Unmarshal(data, &crv1)
|
|
Packit Service |
217d88 |
if err != nil {
|
|
Packit Service |
15f37d |
return err
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
217d88 |
|
|
Packit Service |
217d88 |
*cr = Result(crv1)
|
|
Packit Service |
15f37d |
return nil
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
// Convert new OSBuild v2 format result into a v1 by copying the most useful
|
|
Packit Service |
15f37d |
// values:
|
|
Packit Service |
15f37d |
// - Compose success status
|
|
Packit Service |
15f37d |
// - Output of Stages (Log) as flattened list of v1 StageResults
|
|
Packit Service |
217d88 |
func (cr *Result) fromV2(crv2 osbuild2.Result) {
|
|
Packit Service |
15f37d |
cr.Success = crv2.Success
|
|
Packit Service |
15f37d |
// Empty build and assembler results for new types of jobs
|
|
Packit Service |
15f37d |
cr.Build = new(buildResult)
|
|
Packit Service |
15f37d |
cr.Assembler = new(rawAssemblerResult)
|
|
Packit Service |
15f37d |
|
|
Packit Service |
15f37d |
// convert all stages logs from all pipelines into v1 StageResult objects
|
|
Packit Service |
15f37d |
for pname, stages := range crv2.Log {
|
|
Packit Service |
15f37d |
for idx, stage := range stages {
|
|
Packit Service |
15f37d |
stageResult := StageResult{
|
|
Packit Service |
15f37d |
// Create uniquely identifiable name for the stage:
|
|
Packit Service |
15f37d |
// <pipeline name>:<stage index>-<stage type>
|
|
Packit Service |
15f37d |
Name: fmt.Sprintf("%s:%d-%s", pname, idx, stage.Type),
|
|
Packit Service |
15f37d |
Success: stage.Success,
|
|
Packit Service |
15f37d |
Output: stage.Output,
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
cr.Stages = append(cr.Stages, stageResult)
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
}
|
|
Packit Service |
15f37d |
}
|