debian-forge-composer/internal/compose/compose.go
Ondřej Budai 55d3854033 targets/local: drop Location field
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.
2020-02-14 11:53:38 +01:00

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
}