worker: Define new jobs to handle copying and resharing of images

The copy job copies from one region to another. It does not preserve the
sharing on the ami and it's snapshot, that needs to be queued
separately.
This commit is contained in:
Sanne Raymaekers 2022-06-30 11:46:24 +02:00 committed by Ondřej Budai
parent 5e9ecd3ae4
commit 099b34b301
5 changed files with 405 additions and 42 deletions

View file

@ -0,0 +1,144 @@
package main
import (
"fmt"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/sirupsen/logrus"
"github.com/osbuild/osbuild-composer/internal/cloud/awscloud"
"github.com/osbuild/osbuild-composer/internal/worker"
"github.com/osbuild/osbuild-composer/internal/worker/clienterrors"
)
func getAWS(awsCreds, region string) (*awscloud.AWS, error) {
if awsCreds != "" {
return awscloud.NewFromFile(awsCreds, region)
}
return awscloud.NewDefault(region)
}
type AWSEC2CopyJobImpl struct {
AWSCreds string
}
func (impl *AWSEC2CopyJobImpl) Run(job worker.Job) error {
logWithId := logrus.WithField("jobId", job.Id())
result := worker.AWSEC2CopyJobResult{}
defer func() {
err := job.Update(&result)
if err != nil {
logWithId.Errorf("Error reporting job result: %v", err)
}
}()
var args worker.AWSEC2CopyJob
err := job.Args(&args)
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingJobArgs, fmt.Sprintf("Error parsing arguments: %v", err), nil)
return err
}
aws, err := getAWS(impl.AWSCreds, args.TargetRegion)
if err != nil {
logWithId.Errorf("Error creating aws client: %v", err)
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, "Invalid worker config", nil)
return err
}
ami, err := aws.CopyImage(args.TargetName, args.Ami, args.SourceRegion)
if err != nil {
logWithId.Errorf("Error copying ami: %v", err)
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Error copying ami %s", args.Ami), nil)
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case "InvalidRegion":
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Invalid source region '%s'", args.SourceRegion), nil)
case "InvalidAMIID.Malformed":
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Malformed source ami id '%s'", args.Ami), nil)
case "InvalidAMIID.NotFound":
fallthrough // CopyImage returns InvalidRequest instead of InvalidAMIID.NotFound
case "InvalidRequest":
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Source ami '%s' not found", args.Ami), nil)
}
}
return err
}
result.Ami = ami
result.Region = args.TargetRegion
return nil
}
type AWSEC2ShareJobImpl struct {
AWSCreds string
}
func (impl *AWSEC2ShareJobImpl) Run(job worker.Job) error {
logWithId := logrus.WithField("jobId", job.Id())
result := worker.AWSEC2ShareJobResult{}
defer func() {
err := job.Update(&result)
if err != nil {
logWithId.Errorf("Error reporting job result: %v", err)
}
}()
var args worker.AWSEC2ShareJob
err := job.Args(&args)
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingJobArgs, fmt.Sprintf("Error parsing arguments: %v", err), nil)
return err
}
if args.Ami == "" || args.Region == "" {
if job.NDynamicArgs() != 1 {
logWithId.Error("No arguments given and dynamic args empty")
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorNoDynamicArgs, "An ec2 share job should have args or depend on an ec2 copy job", nil)
return nil
}
var cjResult worker.AWSEC2CopyJobResult
err = job.DynamicArgs(0, &cjResult)
if err != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorParsingDynamicArgs, "Error parsing dynamic args as ec2 copy job", nil)
return err
}
if cjResult.JobError != nil {
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorJobDependency, "AWSEC2CopyJob dependency failed", nil)
return nil
}
args.Ami = cjResult.Ami
args.Region = cjResult.Region
}
aws, err := getAWS(impl.AWSCreds, args.Region)
if err != nil {
logWithId.Errorf("Error creating aws client: %v", err)
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorInvalidConfig, "Invalid worker config", nil)
return err
}
err = aws.ShareImage(args.Ami, args.ShareWithAccounts)
if err != nil {
logWithId.Errorf("Error sharing image: %v", err)
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Error sharing image with target %v", args.ShareWithAccounts), nil)
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case "InvalidAMIID.Malformed":
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Malformed ami id '%s'", args.Ami), nil)
case "InvalidAMIID.NotFound":
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Ami '%s' not found in region '%s'", args.Ami, args.Region), nil)
case "InvalidAMIAttributeItemValue":
result.JobError = clienterrors.WorkerClientError(clienterrors.ErrorSharingTarget, fmt.Sprintf("Invalid user id to share ami with: %v", args.ShareWithAccounts), nil)
}
}
return err
}
result.Ami = args.Ami
result.Region = args.Region
return nil
}

View file

@ -464,6 +464,12 @@ func main() {
worker.JobTypeContainerResolve: &ContainerResolveJobImpl{
AuthFilePath: containersAuthFilePath,
},
worker.JobTypeAWSEC2Copy: &AWSEC2CopyJobImpl{
AWSCreds: awsCredentials,
},
worker.JobTypeAWSEC2Share: &AWSEC2ShareJobImpl{
AWSCreds: awsCredentials,
},
}
acceptedJobTypes := []string{}