|
Packit |
63bb0d |
package awsupload
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit Service |
509fd4 |
"fmt"
|
|
Packit |
63bb0d |
"log"
|
|
Packit |
63bb0d |
"os"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/request"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/aws/session"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/service/ec2"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/service/s3"
|
|
Packit |
63bb0d |
"github.com/aws/aws-sdk-go/service/s3/s3manager"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type AWS struct {
|
|
Packit |
63bb0d |
uploader *s3manager.Uploader
|
|
Packit Service |
509fd4 |
ec2 *ec2.EC2
|
|
Packit |
63bb0d |
s3 *s3.S3
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func New(region, accessKeyID, accessKey string) (*AWS, error) {
|
|
Packit |
63bb0d |
// Session credentials
|
|
Packit |
63bb0d |
creds := credentials.NewStaticCredentials(accessKeyID, accessKey, "")
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Create a Session with a custom region
|
|
Packit |
63bb0d |
sess, err := session.NewSession(&aws.Config{
|
|
Packit |
63bb0d |
Credentials: creds,
|
|
Packit |
63bb0d |
Region: aws.String(region),
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return &AWS{
|
|
Packit |
63bb0d |
uploader: s3manager.NewUploader(sess),
|
|
Packit Service |
509fd4 |
ec2: ec2.New(sess),
|
|
Packit |
63bb0d |
s3: s3.New(sess),
|
|
Packit |
63bb0d |
}, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
func (a *AWS) Upload(filename, bucket, key string) (*s3manager.UploadOutput, error) {
|
|
Packit |
63bb0d |
file, err := os.Open(filename)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
log.Printf("[AWS] 🚀 Uploading image to S3: %s/%s", bucket, key)
|
|
Packit |
63bb0d |
return a.uploader.Upload(
|
|
Packit |
63bb0d |
&s3manager.UploadInput{
|
|
Packit |
63bb0d |
Bucket: aws.String(bucket),
|
|
Packit |
63bb0d |
Key: aws.String(key),
|
|
Packit |
63bb0d |
Body: file,
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// WaitUntilImportSnapshotCompleted uses the Amazon EC2 API operation
|
|
Packit |
63bb0d |
// DescribeImportSnapshots to wait for a condition to be met before returning.
|
|
Packit |
63bb0d |
// If the condition is not met within the max attempt window, an error will
|
|
Packit |
63bb0d |
// be returned.
|
|
Packit |
63bb0d |
func WaitUntilImportSnapshotTaskCompleted(c *ec2.EC2, input *ec2.DescribeImportSnapshotTasksInput) error {
|
|
Packit |
63bb0d |
return WaitUntilImportSnapshotTaskCompletedWithContext(c, aws.BackgroundContext(), input)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// WaitUntilImportSnapshotCompletedWithContext is an extended version of
|
|
Packit |
63bb0d |
// WaitUntilImportSnapshotCompleted. With the support for passing in a
|
|
Packit |
63bb0d |
// context and options to configure the Waiter and the underlying request
|
|
Packit |
63bb0d |
// options.
|
|
Packit |
63bb0d |
//
|
|
Packit |
63bb0d |
// The context must be non-nil and will be used for request cancellation. If
|
|
Packit |
63bb0d |
// the context is nil a panic will occur. In the future the SDK may create
|
|
Packit |
63bb0d |
// sub-contexts for http.Requests. See https://golang.org/pkg/context/
|
|
Packit |
63bb0d |
// for more information on using Contexts.
|
|
Packit |
63bb0d |
//
|
|
Packit |
63bb0d |
// NOTE(mhayden): The MaxAttempts is set to zero here so that we will keep
|
|
Packit |
63bb0d |
// checking the status of the image import until it succeeds or fails. This
|
|
Packit |
63bb0d |
// process can take anywhere from 5 to 60+ minutes depending on how quickly
|
|
Packit |
63bb0d |
// AWS can import the snapshot.
|
|
Packit |
63bb0d |
func WaitUntilImportSnapshotTaskCompletedWithContext(c *ec2.EC2, ctx aws.Context, input *ec2.DescribeImportSnapshotTasksInput, opts ...request.WaiterOption) error {
|
|
Packit |
63bb0d |
w := request.Waiter{
|
|
Packit |
63bb0d |
Name: "WaitUntilImportSnapshotTaskCompleted",
|
|
Packit |
63bb0d |
MaxAttempts: 0,
|
|
Packit |
63bb0d |
Delay: request.ConstantWaiterDelay(15 * time.Second),
|
|
Packit |
63bb0d |
Acceptors: []request.WaiterAcceptor{
|
|
Packit |
63bb0d |
{
|
|
Packit |
63bb0d |
State: request.SuccessWaiterState,
|
|
Packit |
63bb0d |
Matcher: request.PathAllWaiterMatch, Argument: "ImportSnapshotTasks[].SnapshotTaskDetail.Status",
|
|
Packit |
63bb0d |
Expected: "completed",
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
{
|
|
Packit |
63bb0d |
State: request.FailureWaiterState,
|
|
Packit |
63bb0d |
Matcher: request.PathAllWaiterMatch, Argument: "ImportSnapshotTasks[].SnapshotTaskDetail.Status",
|
|
Packit |
63bb0d |
Expected: "deleted",
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
Logger: c.Config.Logger,
|
|
Packit |
63bb0d |
NewRequest: func(opts []request.Option) (*request.Request, error) {
|
|
Packit |
63bb0d |
var inCpy *ec2.DescribeImportSnapshotTasksInput
|
|
Packit |
63bb0d |
if input != nil {
|
|
Packit |
63bb0d |
tmp := *input
|
|
Packit |
63bb0d |
inCpy = &tmp
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
req, _ := c.DescribeImportSnapshotTasksRequest(inCpy)
|
|
Packit |
63bb0d |
req.SetContext(ctx)
|
|
Packit |
63bb0d |
req.ApplyOptions(opts...)
|
|
Packit |
63bb0d |
return req, nil
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
w.ApplyOptions(opts...)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return w.WaitWithContext(ctx)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// Register is a function that imports a snapshot, waits for the snapshot to
|
|
Packit Service |
509fd4 |
// fully import, tags the snapshot, cleans up the image in S3, and registers
|
|
Packit Service |
509fd4 |
// an AMI in AWS.
|
|
Packit Service |
509fd4 |
func (a *AWS) Register(name, bucket, key string, shareWith []string, rpmArch string) (*string, error) {
|
|
Packit Service |
509fd4 |
rpmArchToEC2Arch := map[string]string{
|
|
Packit Service |
509fd4 |
"x86_64": "x86_64",
|
|
Packit Service |
509fd4 |
"aarch64": "arm64",
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
ec2Arch, validArch := rpmArchToEC2Arch[rpmArch]
|
|
Packit Service |
509fd4 |
if !validArch {
|
|
Packit Service |
509fd4 |
return nil, fmt.Errorf("ec2 doesn't support the following arch: %s", rpmArch)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
log.Printf("[AWS] 📥 Importing snapshot from image: %s/%s", bucket, key)
|
|
Packit Service |
509fd4 |
snapshotDescription := fmt.Sprintf("Image Builder AWS Import of %s", name)
|
|
Packit Service |
509fd4 |
importTaskOutput, err := a.ec2.ImportSnapshot(
|
|
Packit |
63bb0d |
&ec2.ImportSnapshotInput{
|
|
Packit Service |
509fd4 |
Description: aws.String(snapshotDescription),
|
|
Packit |
63bb0d |
DiskContainer: &ec2.SnapshotDiskContainer{
|
|
Packit |
63bb0d |
UserBucket: &ec2.UserBucket{
|
|
Packit |
63bb0d |
S3Bucket: aws.String(bucket),
|
|
Packit |
63bb0d |
S3Key: aws.String(key),
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
log.Printf("[AWS] error importing snapshot: %s", err)
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
log.Printf("[AWS] 🚚 Waiting for snapshot to finish importing: %s", *importTaskOutput.ImportTaskId)
|
|
Packit |
63bb0d |
err = WaitUntilImportSnapshotTaskCompleted(
|
|
Packit Service |
509fd4 |
a.ec2,
|
|
Packit |
63bb0d |
&ec2.DescribeImportSnapshotTasksInput{
|
|
Packit |
63bb0d |
ImportTaskIds: []*string{
|
|
Packit |
63bb0d |
importTaskOutput.ImportTaskId,
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// we no longer need the object in s3, let's just delete it
|
|
Packit |
63bb0d |
log.Printf("[AWS] 🧹 Deleting image from S3: %s/%s", bucket, key)
|
|
Packit |
63bb0d |
_, err = a.s3.DeleteObject(&s3.DeleteObjectInput{
|
|
Packit |
63bb0d |
Bucket: aws.String(bucket),
|
|
Packit |
63bb0d |
Key: aws.String(key),
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
importOutput, err := a.ec2.DescribeImportSnapshotTasks(
|
|
Packit |
63bb0d |
&ec2.DescribeImportSnapshotTasksInput{
|
|
Packit |
63bb0d |
ImportTaskIds: []*string{
|
|
Packit |
63bb0d |
importTaskOutput.ImportTaskId,
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
snapshotID := importOutput.ImportSnapshotTasks[0].SnapshotTaskDetail.SnapshotId
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
if len(shareWith) > 0 {
|
|
Packit Service |
509fd4 |
log.Printf("[AWS] 🎥 Sharing ec2 snapshot")
|
|
Packit Service |
509fd4 |
var userIds []*string
|
|
Packit Service |
509fd4 |
for _, v := range shareWith {
|
|
Packit Service |
509fd4 |
userIds = append(userIds, &v)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
_, err := a.ec2.ModifySnapshotAttribute(
|
|
Packit Service |
509fd4 |
&ec2.ModifySnapshotAttributeInput{
|
|
Packit Service |
509fd4 |
Attribute: aws.String("createVolumePermission"),
|
|
Packit Service |
509fd4 |
OperationType: aws.String("add"),
|
|
Packit Service |
509fd4 |
SnapshotId: snapshotID,
|
|
Packit Service |
509fd4 |
UserIds: userIds,
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return nil, err
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
log.Println("[AWS] 📨 Shared ec2 snapshot")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Tag the snapshot with the image name.
|
|
Packit Service |
509fd4 |
req, _ := a.ec2.CreateTagsRequest(
|
|
Packit Service |
509fd4 |
&ec2.CreateTagsInput{
|
|
Packit Service |
509fd4 |
Resources: []*string{snapshotID},
|
|
Packit Service |
509fd4 |
Tags: []*ec2.Tag{
|
|
Packit Service |
509fd4 |
{
|
|
Packit Service |
509fd4 |
Key: aws.String("Name"),
|
|
Packit Service |
509fd4 |
Value: aws.String(name),
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
)
|
|
Packit Service |
509fd4 |
err = req.Send()
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return nil, err
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
log.Printf("[AWS] 📋 Registering AMI from imported snapshot: %s", *snapshotID)
|
|
Packit Service |
509fd4 |
registerOutput, err := a.ec2.RegisterImage(
|
|
Packit |
63bb0d |
&ec2.RegisterImageInput{
|
|
Packit Service |
509fd4 |
Architecture: aws.String(ec2Arch),
|
|
Packit |
63bb0d |
VirtualizationType: aws.String("hvm"),
|
|
Packit |
63bb0d |
Name: aws.String(name),
|
|
Packit |
63bb0d |
RootDeviceName: aws.String("/dev/sda1"),
|
|
Packit |
63bb0d |
EnaSupport: aws.Bool(true),
|
|
Packit |
63bb0d |
BlockDeviceMappings: []*ec2.BlockDeviceMapping{
|
|
Packit |
63bb0d |
{
|
|
Packit |
63bb0d |
DeviceName: aws.String("/dev/sda1"),
|
|
Packit |
63bb0d |
Ebs: &ec2.EbsBlockDevice{
|
|
Packit |
63bb0d |
SnapshotId: snapshotID,
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
},
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
log.Printf("[AWS] 🎉 AMI registered: %s", *registerOutput.ImageId)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
3a6627 |
// Tag the image with the image name.
|
|
Packit Service |
3a6627 |
req, _ = a.ec2.CreateTagsRequest(
|
|
Packit Service |
3a6627 |
&ec2.CreateTagsInput{
|
|
Packit Service |
3a6627 |
Resources: []*string{registerOutput.ImageId},
|
|
Packit Service |
3a6627 |
Tags: []*ec2.Tag{
|
|
Packit Service |
3a6627 |
{
|
|
Packit Service |
3a6627 |
Key: aws.String("Name"),
|
|
Packit Service |
3a6627 |
Value: aws.String(name),
|
|
Packit Service |
3a6627 |
},
|
|
Packit Service |
3a6627 |
},
|
|
Packit Service |
3a6627 |
},
|
|
Packit Service |
3a6627 |
)
|
|
Packit Service |
3a6627 |
err = req.Send()
|
|
Packit Service |
3a6627 |
if err != nil {
|
|
Packit Service |
3a6627 |
return nil, err
|
|
Packit Service |
3a6627 |
}
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
509fd4 |
if len(shareWith) > 0 {
|
|
Packit Service |
509fd4 |
log.Println("[AWS] 💿 Sharing ec2 AMI")
|
|
Packit Service |
509fd4 |
var launchPerms []*ec2.LaunchPermission
|
|
Packit Service |
509fd4 |
for _, id := range shareWith {
|
|
Packit Service |
509fd4 |
launchPerms = append(launchPerms, &ec2.LaunchPermission{
|
|
Packit Service |
509fd4 |
UserId: &id,
|
|
Packit Service |
509fd4 |
})
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
_, err := a.ec2.ModifyImageAttribute(
|
|
Packit Service |
509fd4 |
&ec2.ModifyImageAttributeInput{
|
|
Packit Service |
509fd4 |
ImageId: registerOutput.ImageId,
|
|
Packit Service |
509fd4 |
LaunchPermission: &ec2.LaunchPermissionModifications{
|
|
Packit Service |
509fd4 |
Add: launchPerms,
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
},
|
|
Packit Service |
509fd4 |
)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return nil, err
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
log.Println("[AWS] 💿 Shared AMI")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
return registerOutput.ImageId, nil
|
|
Packit |
63bb0d |
}
|