Cloud API: support cloud upload for Koji composes

Add support to handle upload options in image requests for Koji
composes. The image is always uploaded to Koji, but now it can be
uploaded to the cloud environment in addition to Koji as part of the
build.

The image name used for Koji image can't be used as is for uploading to
the cloud, because each cloud provider has its own requirements for the
valid characters. For now, let the Cloud API implementation generate a
random image name. The name is always returned in the compose status's
upload status, so it should be possible to attach it to the Koji build
to allow users to find the image.
This commit is contained in:
Tomas Hozza 2022-07-01 21:31:52 +02:00 committed by Tom Gundersen
parent 77a1672b79
commit 85f9f07a1f
2 changed files with 79 additions and 46 deletions

View file

@ -299,10 +299,6 @@ func (h *apiHandlers) PostCompose(ctx echo.Context) error {
return HTTPError(ErrorJSONUnMarshallingError)
}
} else {
// TODO: support uploads also for koji
if request.Koji != nil {
return HTTPError(ErrorJSONUnMarshallingError)
}
/* oneOf is not supported by the openapi generator so marshal and unmarshal the uploadrequest based on the type */
switch ir.ImageType {
case ImageTypesAws:
@ -516,6 +512,53 @@ func imageTypeFromApiImageType(it ImageTypes, arch distro.Arch) string {
return ""
}
func targetResultToUploadStatus(t *target.TargetResult) (*UploadStatus, error) {
var us *UploadStatus
var uploadType UploadTypes
var uploadOptions interface{}
switch t.Name {
case target.TargetNameAWS:
uploadType = UploadTypesAws
awsOptions := t.Options.(*target.AWSTargetResultOptions)
uploadOptions = AWSEC2UploadStatus{
Ami: awsOptions.Ami,
Region: awsOptions.Region,
}
case target.TargetNameAWSS3:
uploadType = UploadTypesAwsS3
awsOptions := t.Options.(*target.AWSS3TargetResultOptions)
uploadOptions = AWSS3UploadStatus{
Url: awsOptions.URL,
}
case target.TargetNameGCP:
uploadType = UploadTypesGcp
gcpOptions := t.Options.(*target.GCPTargetResultOptions)
uploadOptions = GCPUploadStatus{
ImageName: gcpOptions.ImageName,
ProjectId: gcpOptions.ProjectID,
}
case target.TargetNameAzureImage:
uploadType = UploadTypesAzure
gcpOptions := t.Options.(*target.AzureImageTargetResultOptions)
uploadOptions = AzureUploadStatus{
ImageName: gcpOptions.ImageName,
}
default:
return nil, fmt.Errorf("unknown upload target: %s", t.Name)
}
us = &UploadStatus{
// TODO: determine upload status based on the target results, not job results
// Don't set the status here for now, but let it be set by the caller.
//Status: UploadStatusValue(result.UploadStatus),
Type: uploadType,
Options: uploadOptions,
}
return us, nil
}
func (h *apiHandlers) GetComposeStatus(ctx echo.Context, id string) error {
return h.server.EnsureJobChannel(h.getComposeStatusImpl)(ctx, id)
}
@ -549,47 +592,13 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
if len(result.TargetResults) != 1 {
return HTTPError(ErrorSeveralUploadTargets)
}
tr := *result.TargetResults[0]
var uploadType UploadTypes
var uploadOptions interface{}
switch tr.Name {
case target.TargetNameAWS:
uploadType = UploadTypesAws
awsOptions := tr.Options.(*target.AWSTargetResultOptions)
uploadOptions = AWSEC2UploadStatus{
Ami: awsOptions.Ami,
Region: awsOptions.Region,
}
case target.TargetNameAWSS3:
uploadType = UploadTypesAwsS3
awsOptions := tr.Options.(*target.AWSS3TargetResultOptions)
uploadOptions = AWSS3UploadStatus{
Url: awsOptions.URL,
}
case target.TargetNameGCP:
uploadType = UploadTypesGcp
gcpOptions := tr.Options.(*target.GCPTargetResultOptions)
uploadOptions = GCPUploadStatus{
ImageName: gcpOptions.ImageName,
ProjectId: gcpOptions.ProjectID,
}
case target.TargetNameAzureImage:
uploadType = UploadTypesAzure
gcpOptions := tr.Options.(*target.AzureImageTargetResultOptions)
uploadOptions = AzureUploadStatus{
ImageName: gcpOptions.ImageName,
}
default:
tr := result.TargetResults[0]
us, err = targetResultToUploadStatus(tr)
if err != nil {
return HTTPError(ErrorUnknownUploadTarget)
}
us = &UploadStatus{
Status: UploadStatusValue(result.UploadStatus),
Type: uploadType,
Options: uploadOptions,
}
// TODO: determine upload status based on the target results, not job results
us.Status = UploadStatusValue(result.UploadStatus)
}
return ctx.JSON(http.StatusOK, ComposeStatus{
@ -631,10 +640,28 @@ func (h *apiHandlers) getComposeStatusImpl(ctx echo.Context, id string) error {
if err != nil {
return HTTPError(ErrorGettingBuildDependencyStatus)
}
var us *UploadStatus
// only single upload target in addition to Koji is allowed
if len(buildJobResult.TargetResults) > 2 {
return HTTPError(ErrorSeveralUploadTargets)
}
for _, tr := range buildJobResult.TargetResults {
if tr.Name != target.TargetNameKoji {
us, err = targetResultToUploadStatus(tr)
if err != nil {
return HTTPError(ErrorUnknownUploadTarget)
}
// TODO: determine upload status based on the target results, not job results
us.Status = UploadStatusValue(buildJobResult.UploadStatus)
}
}
buildJobResults = append(buildJobResults, buildJobResult)
buildJobStatuses = append(buildJobStatuses, ImageStatus{
Status: imageStatusFromKojiJobStatus(buildJobStatus, &initResult, &buildJobResult),
Error: composeStatusErrorFromJobError(buildJobError),
Status: imageStatusFromKojiJobStatus(buildJobStatus, &initResult, &buildJobResult),
Error: composeStatusErrorFromJobError(buildJobError),
UploadStatus: us,
})
}
response := ComposeStatus{

View file

@ -185,12 +185,18 @@ func (s *Server) enqueueKojiCompose(taskID uint64, server, name, version, releas
kojiTarget.OsbuildArtifact.ExportName = ir.imageType.Exports()[0]
kojiTarget.ImageName = kojiFilename
targets := []*target.Target{kojiTarget}
// add any cloud upload target if defined
if ir.target != nil {
targets = append(targets, ir.target)
}
buildID, err := s.workers.EnqueueOSBuildAsDependency(ir.arch.Name(), &worker.OSBuildJob{
PipelineNames: &worker.PipelineNames{
Build: ir.imageType.BuildPipelines(),
Payload: ir.imageType.PayloadPipelines(),
},
Targets: []*target.Target{kojiTarget},
Targets: targets,
ManifestDynArgsIdx: common.IntToPtr(1),
}, []uuid.UUID{initID, manifestJobID}, channel)
if err != nil {