debian-forge-composer/internal/target/target.go
Tomas Hozza 776a54135f worker: move osbuild exports from OSBuildJob to target
The osbuild export is specific to the upload target and different
targets may require using a different export. While osbuild-composer
still does not support multiple exports for osbuild jobs, this prepares
the ground for such support in the future.

The backward compatibility with older implementations of the composer
and workers is kept on the JSON (Un)mashaling level, where the JSON
message is always a super-set of the old and new way of providing the
exports to osbuild job.
2022-07-01 18:55:01 +01:00

288 lines
8.7 KiB
Go

package target
import (
"encoding/json"
"fmt"
"time"
"github.com/google/uuid"
"github.com/osbuild/osbuild-composer/internal/common"
)
type TargetName string
// OsbuildArtifact represents a configuration to produce osbuild artifact
// specific to a target.
type OsbuildArtifact struct {
// Filename of the image as produced by osbuild for a given export
ExportFilename string `json:"export_filename"`
// Name of the osbuild pipeline, which should be exported for this target
ExportName string `json:"export_name"`
}
type Target struct {
Uuid uuid.UUID `json:"uuid"`
// Desired name of the image in the target environment
ImageName string `json:"image_name"`
// Name of the specific target type
Name TargetName `json:"name"`
Created time.Time `json:"created"`
Status common.ImageBuildState `json:"status"`
// Target type specific options
Options TargetOptions `json:"options"`
// Configuration to produce osbuild artifact specific to this target
OsbuildArtifact OsbuildArtifact `json:"osbuild_artifact"`
}
func newTarget(name TargetName, options TargetOptions) *Target {
return &Target{
Uuid: uuid.New(),
Name: name,
Created: time.Now(),
Status: common.IBWaiting,
Options: options,
}
}
type TargetOptions interface {
isTargetOptions()
}
type rawTarget struct {
Uuid uuid.UUID `json:"uuid"`
ImageName string `json:"image_name"`
Name TargetName `json:"name"`
Created time.Time `json:"created"`
Status common.ImageBuildState `json:"status"`
Options json.RawMessage `json:"options"`
// Configuration to produce osbuild artifact specific to this target
OsbuildArtifact OsbuildArtifact `json:"osbuild_artifact"`
}
func (target *Target) UnmarshalJSON(data []byte) error {
var rawTarget rawTarget
err := json.Unmarshal(data, &rawTarget)
if err != nil {
return err
}
var options TargetOptions
switch rawTarget.Name {
case TargetNameAzure:
options = new(AzureTargetOptions)
case TargetNameAWS:
options = new(AWSTargetOptions)
case TargetNameAWSS3:
options = new(AWSS3TargetOptions)
case TargetNameGCP:
options = new(GCPTargetOptions)
case TargetNameAzureImage:
options = new(AzureImageTargetOptions)
// Kept for backward compatibility
case TargetNameLocal:
options = new(LocalTargetOptions)
case TargetNameKoji:
options = new(KojiTargetOptions)
case TargetNameVMWare:
options = new(VMWareTargetOptions)
case TargetNameOCI:
options = new(OCITargetOptions)
case TargetNameContainer:
options = new(ContainerTargetOptions)
case TargetNameWorkerServer:
options = new(WorkerServerTargetOptions)
default:
return fmt.Errorf("unexpected target name: %s", rawTarget.Name)
}
err = json.Unmarshal(rawTarget.Options, options)
if err != nil {
return err
}
target.Uuid = rawTarget.Uuid
target.ImageName = rawTarget.ImageName
target.OsbuildArtifact = rawTarget.OsbuildArtifact
target.Name = rawTarget.Name
target.Created = rawTarget.Created
target.Status = rawTarget.Status
target.Options = options
type compatOptionsType struct {
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
var compat compatOptionsType
err = json.Unmarshal(rawTarget.Options, &compat)
if err != nil {
return err
}
// Kept for backward compatibility
// If the `ExportTarget` is not set in the `Target`, the request is most probably
// coming from an old composer. Copy the value from the target options.
if target.OsbuildArtifact.ExportFilename == "" {
target.OsbuildArtifact.ExportFilename = compat.Filename
}
return nil
}
func (target Target) MarshalJSON() ([]byte, error) {
// We can't use composition of the `TargetOptions` interface into a compatibility
// structure, because the value assigned to the embedded interface type member
// would get marshaled under the name of the type.
var rawOptions []byte
var err error
if target.Options != nil {
switch t := target.Options.(type) {
case *AWSTargetOptions:
type compatOptionsType struct {
*AWSTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
AWSTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *AWSS3TargetOptions:
type compatOptionsType struct {
*AWSS3TargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
AWSS3TargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *AzureTargetOptions:
type compatOptionsType struct {
*AzureTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
AzureTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *GCPTargetOptions:
type compatOptionsType struct {
*GCPTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
GCPTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *AzureImageTargetOptions:
type compatOptionsType struct {
*AzureImageTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
AzureImageTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
// Kept for backward compatibility
case *LocalTargetOptions:
type compatOptionsType struct {
*LocalTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
LocalTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *KojiTargetOptions:
type compatOptionsType struct {
*KojiTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
KojiTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *VMWareTargetOptions:
type compatOptionsType struct {
*VMWareTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
VMWareTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *OCITargetOptions:
type compatOptionsType struct {
*OCITargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
OCITargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *ContainerTargetOptions:
type compatOptionsType struct {
*ContainerTargetOptions
// Deprecated: `Filename` is now set in the target itself as `ExportFilename`, not in its options.
Filename string `json:"filename"`
}
compat := compatOptionsType{
ContainerTargetOptions: t,
Filename: target.OsbuildArtifact.ExportFilename,
}
rawOptions, err = json.Marshal(compat)
case *WorkerServerTargetOptions:
// WorkerServer target does not handle the backward compatibility
// for the Filename in target options, because it was added after
// the incompatible change.
rawOptions, err = json.Marshal(target.Options)
default:
return nil, fmt.Errorf("unexpected target options type: %t", t)
}
// check error from marshaling
if err != nil {
return nil, err
}
}
alias := rawTarget{
Uuid: target.Uuid,
ImageName: target.ImageName,
OsbuildArtifact: target.OsbuildArtifact,
Name: target.Name,
Created: target.Created,
Status: target.Status,
Options: json.RawMessage(rawOptions),
}
return json.Marshal(alias)
}