This is not a behavioral change, as all distros currently use empty source objects. But when we move over to rpm-based pipelines, this will change. Make the same change to osbuild-pipeline, so these stay in sync. Signed-off-by: Tom Gundersen <teg@jklm.no>
171 lines
5.4 KiB
Go
171 lines
5.4 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 (
|
|
"time"
|
|
|
|
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
|
"github.com/osbuild/osbuild-composer/internal/common"
|
|
"github.com/osbuild/osbuild-composer/internal/osbuild"
|
|
"github.com/osbuild/osbuild-composer/internal/target"
|
|
)
|
|
|
|
type StateTransitionError struct {
|
|
message string
|
|
}
|
|
|
|
func (ste *StateTransitionError) Error() string {
|
|
return ste.message
|
|
}
|
|
|
|
// 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"`
|
|
Manifest *osbuild.Manifest `json:"manifest"`
|
|
Targets []*target.Target `json:"targets"`
|
|
JobCreated time.Time `json:"job_created"`
|
|
JobStarted time.Time `json:"job_started"`
|
|
JobFinished time.Time `json:"job_finished"`
|
|
Size uint64 `json:"size"`
|
|
}
|
|
|
|
// DeepCopy creates a copy of the ImageBuild structure
|
|
func (ib *ImageBuild) DeepCopy() ImageBuild {
|
|
var newManifestPtr *osbuild.Manifest = nil
|
|
if ib.Manifest != nil {
|
|
manifestCopy := *ib.Manifest
|
|
newManifestPtr = &manifestCopy
|
|
}
|
|
var newTargets []*target.Target
|
|
for _, t := range ib.Targets {
|
|
newTarget := *t
|
|
newTargets = append(newTargets, &newTarget)
|
|
}
|
|
// Create new image build struct
|
|
return ImageBuild{
|
|
Id: ib.Id,
|
|
Distro: ib.Distro,
|
|
QueueStatus: ib.QueueStatus,
|
|
ImageType: ib.ImageType,
|
|
Manifest: newManifestPtr,
|
|
Targets: newTargets,
|
|
JobCreated: ib.JobCreated,
|
|
JobStarted: ib.JobStarted,
|
|
JobFinished: ib.JobFinished,
|
|
Size: ib.Size,
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|