When support for osbuild result was added into osbuild-composer it was in a bit hacky way - localtarget's location was reused as a path for the result. This didn't make much sense because we want to store the result even when image build has no localtarget. Several past commits made store less dependant on the localtarget. The responsibility for "holding the paths" to build artifacts was gradually switched from the localtarget to the store while still maintaining backwards compatibility - localtarget.Location still pointed at the correct location. This commit finishes the switch: local target now has no Location field. The store is now fully responsible for managing the artifacts and paths to them. LocalTarget is now just a simple "switch" - if image build has it, then worker uploads an image into the store and it's then available for download using the weldr API.
182 lines
5.7 KiB
Go
182 lines
5.7 KiB
Go
// Package compose encapsulates the concept of a compose. It is a separate module from common, because it includes
|
|
// target which in turn includes common and thus it would create a cyclic dependency, which is forbidden in golang.
|
|
package compose
|
|
|
|
import (
|
|
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
|
"github.com/osbuild/osbuild-composer/internal/common"
|
|
"github.com/osbuild/osbuild-composer/internal/pipeline"
|
|
"github.com/osbuild/osbuild-composer/internal/target"
|
|
"time"
|
|
)
|
|
|
|
type StateTransitionError struct {
|
|
message string
|
|
}
|
|
|
|
func (ste *StateTransitionError) Error() string {
|
|
return ste.message
|
|
}
|
|
|
|
// Image represents the image resulting from a compose.
|
|
type Image struct {
|
|
Path string
|
|
Mime string
|
|
Size uint64
|
|
}
|
|
|
|
// ImageBuild represents a single image build inside a compose
|
|
type ImageBuild struct {
|
|
Id int `json:"id"`
|
|
Distro common.Distribution `json:"distro"`
|
|
QueueStatus common.ImageBuildState `json:"queue_status"`
|
|
ImageType common.ImageType `json:"image_type"`
|
|
Pipeline *pipeline.Pipeline `json:"pipeline"`
|
|
Targets []*target.Target `json:"targets"`
|
|
JobCreated time.Time `json:"job_created"`
|
|
JobStarted time.Time `json:"job_started"`
|
|
JobFinished time.Time `json:"job_finished"`
|
|
Image *Image `json:"image"`
|
|
Size uint64 `json:"size"`
|
|
}
|
|
|
|
// DeepCopy creates a copy of the ImageBuild structure
|
|
func (ib *ImageBuild) DeepCopy() ImageBuild {
|
|
var newImagePtr *Image = nil
|
|
if ib.Image != nil {
|
|
imageCopy := *ib.Image
|
|
newImagePtr = &imageCopy
|
|
}
|
|
var newPipelinePtr *pipeline.Pipeline = nil
|
|
if ib.Pipeline != nil {
|
|
pipelineCopy := *ib.Pipeline
|
|
newPipelinePtr = &pipelineCopy
|
|
}
|
|
var newTargets []*target.Target
|
|
for _, t := range ib.Targets {
|
|
newTarget := *t
|
|
newTargets = append(newTargets, &newTarget)
|
|
}
|
|
// Create new image build struct
|
|
return ImageBuild{
|
|
Distro: ib.Distro,
|
|
QueueStatus: ib.QueueStatus,
|
|
ImageType: ib.ImageType,
|
|
Pipeline: newPipelinePtr,
|
|
Targets: newTargets,
|
|
JobCreated: ib.JobCreated,
|
|
JobStarted: ib.JobStarted,
|
|
JobFinished: ib.JobFinished,
|
|
Image: newImagePtr,
|
|
}
|
|
}
|
|
|
|
func (ib *ImageBuild) HasLocalTarget() bool {
|
|
for _, t := range ib.Targets {
|
|
if _, ok := t.Options.(*target.LocalTargetOptions); ok {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// A Compose represent the task of building a set of images from a single blueprint.
|
|
// It contains all the information necessary to generate the inputs for the job, as
|
|
// well as the job's state.
|
|
type Compose struct {
|
|
Blueprint *blueprint.Blueprint `json:"blueprint"`
|
|
ImageBuilds []ImageBuild `json:"image_builds"`
|
|
}
|
|
|
|
// DeepCopy creates a copy of the Compose structure
|
|
func (c *Compose) DeepCopy() Compose {
|
|
var newBpPtr *blueprint.Blueprint = nil
|
|
if c.Blueprint != nil {
|
|
bpCopy := *c.Blueprint
|
|
newBpPtr = &bpCopy
|
|
}
|
|
newImageBuilds := []ImageBuild{}
|
|
for _, ib := range c.ImageBuilds {
|
|
newImageBuilds = append(newImageBuilds, ib.DeepCopy())
|
|
}
|
|
return Compose{
|
|
Blueprint: newBpPtr,
|
|
ImageBuilds: newImageBuilds,
|
|
}
|
|
}
|
|
|
|
func anyImageBuild(fn func(common.ImageBuildState) bool, list []common.ImageBuildState) bool {
|
|
acc := false
|
|
for _, i := range list {
|
|
if fn(i) {
|
|
acc = true
|
|
}
|
|
}
|
|
return acc
|
|
}
|
|
|
|
func allImageBuilds(fn func(common.ImageBuildState) bool, list []common.ImageBuildState) bool {
|
|
acc := true
|
|
for _, i := range list {
|
|
if !fn(i) {
|
|
acc = false
|
|
}
|
|
}
|
|
return acc
|
|
}
|
|
|
|
// GetState returns a state of the whole compose which is derived from the states of
|
|
// individual image builds inside the compose
|
|
func (c *Compose) GetState() common.ComposeState {
|
|
var imageBuildsStates []common.ImageBuildState
|
|
for _, ib := range c.ImageBuilds {
|
|
imageBuildsStates = append(imageBuildsStates, ib.QueueStatus)
|
|
}
|
|
// In case all states are the same
|
|
if allImageBuilds(func(ib common.ImageBuildState) bool { return ib == common.IBWaiting }, imageBuildsStates) {
|
|
return common.CWaiting
|
|
}
|
|
if allImageBuilds(func(ib common.ImageBuildState) bool { return ib == common.IBFinished }, imageBuildsStates) {
|
|
return common.CFinished
|
|
}
|
|
if allImageBuilds(func(ib common.ImageBuildState) bool { return ib == common.IBFailed }, imageBuildsStates) {
|
|
return common.CFailed
|
|
}
|
|
// In case the states are mixed
|
|
// TODO: can this condition be removed because it is already covered by the default?
|
|
if anyImageBuild(func(ib common.ImageBuildState) bool { return ib == common.IBRunning }, imageBuildsStates) {
|
|
return common.CRunning
|
|
}
|
|
if allImageBuilds(func(ib common.ImageBuildState) bool { return ib == common.IBFailed || ib == common.IBFinished }, imageBuildsStates) {
|
|
return common.CFailed
|
|
}
|
|
// Default value
|
|
return common.CRunning
|
|
}
|
|
|
|
// UpdateState changes a state of a single image build inside the Compose
|
|
func (c *Compose) UpdateState(imageBuildId int, newState common.ImageBuildState) error {
|
|
switch newState {
|
|
case common.IBWaiting:
|
|
return &StateTransitionError{"image build cannot be moved into waiting state"}
|
|
case common.IBRunning:
|
|
if c.ImageBuilds[imageBuildId].QueueStatus == common.IBWaiting || c.ImageBuilds[imageBuildId].QueueStatus == common.IBRunning {
|
|
c.ImageBuilds[imageBuildId].QueueStatus = newState
|
|
} else {
|
|
return &StateTransitionError{"only waiting image build can be transitioned into running state"}
|
|
}
|
|
case common.IBFinished, common.IBFailed:
|
|
if c.ImageBuilds[imageBuildId].QueueStatus == common.IBRunning {
|
|
c.ImageBuilds[imageBuildId].QueueStatus = newState
|
|
for _, t := range c.ImageBuilds[imageBuildId].Targets {
|
|
t.Status = newState
|
|
}
|
|
} else {
|
|
return &StateTransitionError{"only running image build can be transitioned into finished or failed state"}
|
|
}
|
|
default:
|
|
return &StateTransitionError{"invalid state"}
|
|
}
|
|
return nil
|
|
}
|