internal/awscloud: use AWS.Register() from osbuild/images

Signed-off-by: Tomáš Hozza <thozza@redhat.com>
This commit is contained in:
Tomáš Hozza 2025-08-06 13:46:59 +02:00 committed by Tomáš Hozza
parent 27bae770e9
commit a3937e99ce
6 changed files with 52 additions and 225 deletions

View file

@ -217,166 +217,6 @@ func NewForEndpointFromFile(filename, endpoint, region, caBundle string, skipSSL
return newAwsFromCredsWithEndpoint(config.WithSharedCredentialsFiles([]string{filename, "default"}), region, endpoint, caBundle, skipSSLVerification, imagesAWS)
}
// Register is a function that imports a snapshot, waits for the snapshot to
// fully import, tags the snapshot, cleans up the image in S3, and registers
// an AMI in AWS.
// The caller can optionally specify the boot mode of the AMI. If the boot
// mode is not specified, then the instances launched from this AMI use the
// default boot mode value of the instance type.
func (a *AWS) Register(name, bucket, key string, shareWith []string, rpmArch string, bootMode *string) (*string, error) {
rpmArchToEC2Arch := map[string]ec2types.ArchitectureValues{
"x86_64": ec2types.ArchitectureValuesX8664,
"aarch64": ec2types.ArchitectureValuesArm64,
}
ec2Arch, validArch := rpmArchToEC2Arch[rpmArch]
if !validArch {
return nil, fmt.Errorf("ec2 doesn't support the following arch: %s", rpmArch)
}
bootModeToEC2BootMode := map[string]ec2types.BootModeValues{
string(ec2types.BootModeValuesLegacyBios): ec2types.BootModeValuesLegacyBios,
string(ec2types.BootModeValuesUefi): ec2types.BootModeValuesUefi,
string(ec2types.BootModeValuesUefiPreferred): ec2types.BootModeValuesUefiPreferred,
}
ec2BootMode := ec2types.BootModeValuesUefiPreferred
if bootMode != nil {
bm, validBootMode := bootModeToEC2BootMode[*bootMode]
if !validBootMode {
return nil, fmt.Errorf("ec2 doesn't support the following boot mode: %s", *bootMode)
}
ec2BootMode = bm
}
logrus.Infof("[AWS] 📥 Importing snapshot from image: %s/%s", bucket, key)
snapshotDescription := fmt.Sprintf("Image Builder AWS Import of %s", name)
importTaskOutput, err := a.ec2.ImportSnapshot(
context.Background(),
&ec2.ImportSnapshotInput{
Description: aws.String(snapshotDescription),
DiskContainer: &ec2types.SnapshotDiskContainer{
UserBucket: &ec2types.UserBucket{
S3Bucket: aws.String(bucket),
S3Key: aws.String(key),
},
},
},
)
if err != nil {
logrus.Warnf("[AWS] error importing snapshot: %s", err)
return nil, err
}
logrus.Infof("[AWS] 🚚 Waiting for snapshot to finish importing: %s", *importTaskOutput.ImportTaskId)
// importTaskOutput.
snapWaiter := ec2.NewSnapshotImportedWaiter(a.ec2)
snapWaitOutput, err := snapWaiter.WaitForOutput(
context.Background(),
&ec2.DescribeImportSnapshotTasksInput{
ImportTaskIds: []string{
*importTaskOutput.ImportTaskId,
},
},
time.Hour*24,
)
if err != nil {
return nil, err
}
snapshotTaskStatus := *snapWaitOutput.ImportSnapshotTasks[0].SnapshotTaskDetail.Status
if snapshotTaskStatus != "completed" {
return nil, fmt.Errorf("Unable to import snapshot, task result: %v, msg: %v", snapshotTaskStatus, *snapWaitOutput.ImportSnapshotTasks[0].SnapshotTaskDetail.StatusMessage)
}
// we no longer need the object in s3, let's just delete it
logrus.Infof("[AWS] 🧹 Deleting image from S3: %s/%s", bucket, key)
_, err = a.s3.DeleteObject(
context.Background(),
&s3.DeleteObjectInput{
Bucket: aws.String(bucket),
Key: aws.String(key),
},
)
if err != nil {
return nil, err
}
snapshotID := *snapWaitOutput.ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId
// Tag the snapshot with the image name.
_, err = a.ec2.CreateTags(
context.Background(),
&ec2.CreateTagsInput{
Resources: []string{snapshotID},
Tags: []ec2types.Tag{
{
Key: aws.String("Name"),
Value: aws.String(name),
},
},
},
)
if err != nil {
return nil, err
}
logrus.Infof("[AWS] 📋 Registering AMI from imported snapshot: %s", snapshotID)
registerOutput, err := a.ec2.RegisterImage(
context.Background(),
&ec2.RegisterImageInput{
Architecture: ec2Arch,
BootMode: ec2BootMode,
VirtualizationType: aws.String("hvm"),
Name: aws.String(name),
RootDeviceName: aws.String("/dev/sda1"),
EnaSupport: aws.Bool(true),
BlockDeviceMappings: []ec2types.BlockDeviceMapping{
{
DeviceName: aws.String("/dev/sda1"),
Ebs: &ec2types.EbsBlockDevice{
SnapshotId: aws.String(snapshotID),
},
},
},
},
)
if err != nil {
return nil, err
}
logrus.Infof("[AWS] 🎉 AMI registered: %s", *registerOutput.ImageId)
// Tag the image with the image name.
_, err = a.ec2.CreateTags(
context.Background(),
&ec2.CreateTagsInput{
Resources: []string{*registerOutput.ImageId},
Tags: []ec2types.Tag{
{
Key: aws.String("Name"),
Value: aws.String(name),
},
},
},
)
if err != nil {
return nil, err
}
if len(shareWith) > 0 {
err = a.shareSnapshot(snapshotID, shareWith)
if err != nil {
return nil, err
}
err = a.shareImage(registerOutput.ImageId, shareWith)
if err != nil {
return nil, err
}
}
return registerOutput.ImageId, nil
}
// target region is determined by the region configured in the aws session
func (a *AWS) CopyImage(name, ami, sourceRegion string) (string, error) {
result, err := a.ec2.CopyImage(

View file

@ -6,40 +6,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/osbuild/osbuild-composer/internal/cloud/awscloud"
"github.com/osbuild/osbuild-composer/internal/common"
)
func TestEC2Register(t *testing.T) {
m := newEc2Mock(t)
aws := awscloud.NewForTest(m, nil, &s3mock{t, "bucket", "object-key"}, nil, nil)
require.NotNil(t, aws)
// Image without share
imageId, err := aws.Register("image-name", "bucket", "object-key", []string{}, "x86_64", common.ToPtr("uefi-preferred"))
require.NoError(t, err)
require.Equal(t, "image-id", *imageId)
// basic image import operations
require.Equal(t, 1, m.calledFn["ImportSnapshot"])
require.Equal(t, 1, m.calledFn["RegisterImage"])
// sharing operations
require.Equal(t, 0, m.calledFn["ModifyImageAttribute"])
require.Equal(t, 0, m.calledFn["ModifySnapshotAttribute"])
// Image with share
imageId, err = aws.Register("image-name", "bucket", "object-key", []string{"share-with-user"}, "x86_64", common.ToPtr("uefi-preferred"))
require.NoError(t, err)
require.Equal(t, "image-id", *imageId)
// basic image import operations
require.Equal(t, 2, m.calledFn["ImportSnapshot"])
require.Equal(t, 2, m.calledFn["RegisterImage"])
// sharing operations
require.Equal(t, 1, m.calledFn["ModifyImageAttribute"])
require.Equal(t, 1, m.calledFn["ModifySnapshotAttribute"])
// 2 snapshots, 2 images
require.Equal(t, 4, m.calledFn["CreateTags"])
}
func TestEC2CopyImage(t *testing.T) {
m := newEc2Mock(t)
aws := awscloud.NewForTest(m, nil, &s3mock{t, "bucket", "object-key"}, nil, nil)

View file

@ -42,7 +42,6 @@ type EC2 interface {
// Images
CopyImage(context.Context, *ec2.CopyImageInput, ...func(*ec2.Options)) (*ec2.CopyImageOutput, error)
RegisterImage(context.Context, *ec2.RegisterImageInput, ...func(*ec2.Options)) (*ec2.RegisterImageOutput, error)
DeregisterImage(context.Context, *ec2.DeregisterImageInput, ...func(*ec2.Options)) (*ec2.DeregisterImageOutput, error)
DescribeImages(context.Context, *ec2.DescribeImagesInput, ...func(*ec2.Options)) (*ec2.DescribeImagesOutput, error)
ModifyImageAttribute(context.Context, *ec2.ModifyImageAttributeInput, ...func(*ec2.Options)) (*ec2.ModifyImageAttributeOutput, error)
@ -50,7 +49,6 @@ type EC2 interface {
// Snapshots
DeleteSnapshot(context.Context, *ec2.DeleteSnapshotInput, ...func(*ec2.Options)) (*ec2.DeleteSnapshotOutput, error)
DescribeImportSnapshotTasks(context.Context, *ec2.DescribeImportSnapshotTasksInput, ...func(*ec2.Options)) (*ec2.DescribeImportSnapshotTasksOutput, error)
ImportSnapshot(context.Context, *ec2.ImportSnapshotInput, ...func(*ec2.Options)) (*ec2.ImportSnapshotOutput, error)
ModifySnapshotAttribute(context.Context, *ec2.ModifySnapshotAttributeInput, ...func(*ec2.Options)) (*ec2.ModifySnapshotAttributeOutput, error)
// Tags

View file

@ -331,13 +331,6 @@ func (m *ec2mock) CopyImage(ctx context.Context, input *ec2.CopyImageInput, optf
}, nil
}
func (m *ec2mock) RegisterImage(ctx context.Context, input *ec2.RegisterImageInput, optfns ...func(*ec2.Options)) (*ec2.RegisterImageOutput, error) {
m.calledFn["RegisterImage"] += 1
return &ec2.RegisterImageOutput{
ImageId: &m.imageId,
}, nil
}
func (m *ec2mock) DeregisterImage(ctx context.Context, input *ec2.DeregisterImageInput, optfns ...func(*ec2.Options)) (*ec2.DeregisterImageOutput, error) {
m.calledFn["DeregisterImage"] += 1
return nil, nil
@ -387,13 +380,6 @@ func (m *ec2mock) DescribeImportSnapshotTasks(ctx context.Context, input *ec2.De
}, nil
}
func (m *ec2mock) ImportSnapshot(ctx context.Context, input *ec2.ImportSnapshotInput, optfns ...func(*ec2.Options)) (*ec2.ImportSnapshotOutput, error) {
m.calledFn["ImportSnapshot"] += 1
return &ec2.ImportSnapshotOutput{
ImportTaskId: aws.String("import-task-id"),
}, nil
}
func (m *ec2mock) ModifySnapshotAttribute(ctx context.Context, input *ec2.ModifySnapshotAttributeInput, optfns ...func(*ec2.Options)) (*ec2.ModifySnapshotAttributeOutput, error) {
m.calledFn["ModifySnapshotAttribute"] += 1
return nil, nil