Skip to content

Commit

Permalink
Fetch latest E2E test AMI ID dynamically
Browse files Browse the repository at this point in the history
  • Loading branch information
abhay-krishna committed Jun 1, 2024
1 parent 8e85e1e commit 9ba3d28
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 9 deletions.
1 change: 0 additions & 1 deletion cmd/integration_test/build/script/create_infra_config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ cat << EOF > ${INTEGRATION_TEST_INFRA_CONFIG}
---
ec2:
amiId: ${INTEGRATION_TEST_AL2_AMI_ID}
subnetId: ${INTEGRATION_TEST_SUBNET_ID}
vSphere:
Expand Down
73 changes: 68 additions & 5 deletions internal/pkg/ec2/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"fmt"
"math/rand"
"reflect"
"time"

"github.com/aws/aws-sdk-go/aws"
Expand All @@ -15,6 +16,13 @@ import (
"github.com/aws/eks-anywhere/pkg/retrier"
)

const (
e2eTestAMINameFilter = "eksa-integration-test-AL2-*"
e2eTestAMIDescriptionFilter = "*Kernel version 5.X*"
e2eTestAMIOwner = "857151390494"
timeLayout = time.RFC3339
)

var dockerLogsUserData = `
#!/bin/bash
cat <<'EOF' >> /etc/docker/daemon.json
Expand All @@ -26,7 +34,7 @@ EOF
systemctl restart docker --no-block
`

func CreateInstance(session *session.Session, amiId, key, tag, instanceProfileName, subnetId, name string) (string, error) {
func CreateInstance(session *session.Session, key, tag, instanceProfileName, subnetID, name string) (string, error) {

Check failure on line 37 in internal/pkg/ec2/create.go

View workflow job for this annotation

GitHub Actions / lint

exported: exported function CreateInstance should have comment or be unexported (revive)

Check warning on line 37 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L37

Added line #L37 was not covered by tests
r := retrier.New(180*time.Minute, retrier.WithBackoffFactor(1.5), retrier.WithRetryPolicy(func(totalRetries int, err error) (retry bool, wait time.Duration) {
// EC2 Request token bucket has a refill rate of 2 request tokens
// per second, so waiting between 5 and 10 seconds per retry with a backoff factor of 1.5 should be sufficient
Expand All @@ -41,12 +49,40 @@ func CreateInstance(session *session.Session, amiId, key, tag, instanceProfileNa
}))

service := ec2.New(session)
var result *ec2.Reservation

err := r.Retry(func() error {
describeImagesOutput, err := service.DescribeImages(&ec2.DescribeImagesInput{
Filters: []*ec2.Filter{

Check warning on line 54 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L53-L54

Added lines #L53 - L54 were not covered by tests
{
Name: aws.String("name"),
Values: []*string{
aws.String(e2eTestAMINameFilter),
},
},

Check warning on line 60 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L56-L60

Added lines #L56 - L60 were not covered by tests
{
Name: aws.String("description"),
Values: []*string{
aws.String(e2eTestAMIDescriptionFilter),
},
},
},
Owners: []*string{
aws.String(e2eTestAMIOwner),
},
})
if err != nil {
return "", fmt.Errorf("describing images to retrieve AMI ID: %v", err)

Check warning on line 73 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L62-L73

Added lines #L62 - L73 were not covered by tests
}

amiID, err := getLatestAMIID(describeImagesOutput.Images)
if err != nil {
return "", fmt.Errorf("getting latest AMI ID by creation time: %v", err)

Check warning on line 78 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L76-L78

Added lines #L76 - L78 were not covered by tests
}

var result *ec2.Reservation
err = r.Retry(func() error {

Check warning on line 82 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L81-L82

Added lines #L81 - L82 were not covered by tests
var err error
result, err = service.RunInstances(&ec2.RunInstancesInput{
ImageId: aws.String(amiId),
ImageId: aws.String(amiID),

Check warning on line 85 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L85

Added line #L85 was not covered by tests
InstanceType: aws.String("t3.2xlarge"),
MinCount: aws.Int64(1),
MaxCount: aws.Int64(1),
Expand All @@ -61,7 +97,7 @@ func CreateInstance(session *session.Session, amiId, key, tag, instanceProfileNa
IamInstanceProfile: &ec2.IamInstanceProfileSpecification{
Name: aws.String(instanceProfileName),
},
SubnetId: aws.String(subnetId),
SubnetId: aws.String(subnetID),

Check warning on line 100 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L100

Added line #L100 was not covered by tests
TagSpecifications: []*ec2.TagSpecification{
{
ResourceType: aws.String("instance"),
Expand Down Expand Up @@ -111,3 +147,30 @@ func isThrottleError(err error) bool {

return false
}

func getLatestAMIID(images []*ec2.Image) (string, error) {
latestImage := &ec2.Image{}
latestImage.CreationDate = aws.String(time.Time{}.Format(timeLayout))
for _, image := range images {
if image.CreationDate != nil {
imageCreationTime, err := time.Parse(timeLayout, *image.CreationDate)
if err != nil {
return "", fmt.Errorf("parsing creation timestamp for image: %v", err)
}

latestImageCreationTime, err := time.Parse(timeLayout, *latestImage.CreationDate)
if err != nil {
return "", fmt.Errorf("parsing creation timestamp for current latest image: %v", err)

Check warning on line 163 in internal/pkg/ec2/create.go

View check run for this annotation

Codecov / codecov/patch

internal/pkg/ec2/create.go#L163

Added line #L163 was not covered by tests
}
if latestImageCreationTime.Before(imageCreationTime) {
latestImage = image
}
}
}

if reflect.DeepEqual(latestImage, &ec2.Image{CreationDate: aws.String(time.Time{}.Format(timeLayout))}) {
return "", fmt.Errorf("no matching AMIs found")
}

return *latestImage.ImageId, nil
}
72 changes: 72 additions & 0 deletions internal/pkg/ec2/create_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package ec2

import (
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
)

const incorrectTimeLayout = time.RFC1123

func TestGetLatestAMIID(t *testing.T) {
tests := []struct {
name string
images []*ec2.Image
wantAMIID string
wantErr bool
}{
{
name: "Get latest image ID success",
images: []*ec2.Image{
{
ImageId: aws.String("ami-01234"),
CreationDate: aws.String(time.Now().Format(timeLayout)),
},
{
ImageId: aws.String("ami-56789"),
CreationDate: aws.String(time.Time{}.Format(timeLayout)),
},
},
wantAMIID: "ami-01234",
wantErr: false,
},
{
name: "Get latest image ID failure - incorrect time layout",
images: []*ec2.Image{
{
ImageId: aws.String("ami-01234"),
CreationDate: aws.String(time.Now().Format(timeLayout)),
},
{
ImageId: aws.String("ami-56789"),
CreationDate: aws.String(time.Time{}.Format(incorrectTimeLayout)),
},
},
wantAMIID: "",
wantErr: true,
},
{
name: "Get latest image ID failure - empty images list",
images: []*ec2.Image{},
wantAMIID: "",
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotImageID, err := getLatestAMIID(tt.images)
if (err != nil) != tt.wantErr {
t.Errorf("getLatestAMIID() error = %v, wantErr %v", err, tt.wantErr)
return
}
if err == nil {
if gotImageID != tt.wantAMIID {
t.Errorf("getLatestAMIID() got = %v, want %v", gotImageID, tt.wantAMIID)
}
}
})
}
}
3 changes: 1 addition & 2 deletions internal/test/e2e/testRunner.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ type testRunner struct {

type Ec2TestRunner struct {
testRunner
AmiID string `yaml:"amiId"`
SubnetID string `yaml:"subnetId"`
}

Expand Down Expand Up @@ -187,7 +186,7 @@ func (v *VSphereTestRunner) createInstance(c instanceRunConf) (string, error) {
func (e *Ec2TestRunner) createInstance(c instanceRunConf) (string, error) {
name := getTestRunnerName(e.logger, c.JobID)
e.logger.V(1).Info("Creating ec2 Test Runner instance", "name", name)
instanceID, err := ec2.CreateInstance(c.Session, e.AmiID, key, tag, c.InstanceProfileName, e.SubnetID, name)
instanceID, err := ec2.CreateInstance(c.Session, key, tag, c.InstanceProfileName, e.SubnetID, name)
if err != nil {
return "", fmt.Errorf("creating instance for e2e tests: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/e2e_test_docker.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ cat << EOF > ${INTEGRATION_TEST_INFRA_CONFIG}
---
ec2:
amiId: ${INTEGRATION_TEST_AL2_AMI_ID}
amiId:
subnetId:
EOF
Expand Down

0 comments on commit 9ba3d28

Please sign in to comment.