Availability zone randomisation

This commit is contained in:
schulze 2023-04-17 15:54:42 +02:00
parent 3fb51d8f84
commit e8962c3cba
2 changed files with 55 additions and 5 deletions

View File

@ -7,6 +7,7 @@ import (
"github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ec2"
"github.com/google/uuid"
"github.com/thefeli73/polemos/state" "github.com/thefeli73/polemos/state"
) )
@ -38,6 +39,7 @@ func AWSUpdateService(config state.Config, region string, service state.CustomUU
// AWSMoveInstance moves a specified instance to a new availability region // AWSMoveInstance moves a specified instance to a new availability region
func AWSMoveInstance(config state.Config) (state.Config) { func AWSMoveInstance(config state.Config) (state.Config) {
// pseudorandom instance from all services for testing // pseudorandom instance from all services for testing
var serviceUUID state.CustomUUID var serviceUUID state.CustomUUID
var instance state.Service var instance state.Service
@ -47,7 +49,7 @@ func AWSMoveInstance(config state.Config) (state.Config) {
break break
} }
fmt.Println("MTD move service:\t", serviceUUID) fmt.Println("MTD move service:\t", uuid.UUID.String(uuid.UUID(serviceUUID)))
region, instanceID := DecodeCloudID(instance.CloudID) region, instanceID := DecodeCloudID(instance.CloudID)
awsConfig := NewConfig(region, config.AWS.CredentialsPath) awsConfig := NewConfig(region, config.AWS.CredentialsPath)
@ -73,7 +75,7 @@ func AWSMoveInstance(config state.Config) (state.Config) {
} }
fmt.Println("Image is ready:\t\t", imageName) fmt.Println("Image is ready:\t\t", imageName)
newInstanceID, err := launchInstance(svc, realInstance, imageName) newInstanceID, err := launchInstance(svc, realInstance, imageName, region)
if err != nil { if err != nil {
fmt.Println("Error launching instance:\t", err) fmt.Println("Error launching instance:\t", err)
return config return config

View File

@ -4,6 +4,7 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"math/rand"
"os" "os"
"strings" "strings"
"time" "time"
@ -141,14 +142,18 @@ func waitForImageReady(svc *ec2.Client, imageID string, timeout time.Duration) e
} }
} }
// launchInstance launches a instance based on an oldInstance and AMI (duplicating the instance) // launchInstance launches a instance IN RANDOM AVAILABILITY ZONE within the same region, based on an oldInstance and AMI (duplicating the instance)
func launchInstance(svc *ec2.Client, oldInstance *types.Instance, imageID string) (string, error) { func launchInstance(svc *ec2.Client, oldInstance *types.Instance, imageID string, region string) (string, error) {
securityGroupIds := make([]string, len(oldInstance.SecurityGroups)) securityGroupIds := make([]string, len(oldInstance.SecurityGroups))
for i, sg := range oldInstance.SecurityGroups { for i, sg := range oldInstance.SecurityGroups {
securityGroupIds[i] = aws.ToString(sg.GroupId) securityGroupIds[i] = aws.ToString(sg.GroupId)
} }
// TODO: select random zone that is not the current one. // TODO: select random zone that is not the current one.
availabilityZone := "us-east-1d" availabilityZone, err := getRandomDifferentAvailabilityZone(svc, oldInstance, region)
if err != nil {
return "", err
}
input := &ec2.RunInstancesInput{ input := &ec2.RunInstancesInput{
ImageId: aws.String(imageID), ImageId: aws.String(imageID),
InstanceType: oldInstance.InstanceType, InstanceType: oldInstance.InstanceType,
@ -171,6 +176,49 @@ func launchInstance(svc *ec2.Client, oldInstance *types.Instance, imageID string
return aws.ToString(output.Instances[0].InstanceId), nil return aws.ToString(output.Instances[0].InstanceId), nil
} }
// getRandomDifferentAvailabilityZone fetches all AZ from the same region as the instance and returns a random AZ that is not equal to the one used by the instance
func getRandomDifferentAvailabilityZone(svc *ec2.Client, instance *types.Instance, region string) (string, error) {
// Seed the random generator
rand.Seed(time.Now().UnixNano())
// Get the current availability zone of the instance
currentAZ := aws.ToString(instance.Placement.AvailabilityZone)
// Describe availability zones in the region
input := &ec2.DescribeAvailabilityZonesInput{
Filters: []types.Filter{
{
Name: aws.String("region-name"),
Values: []string{region},
},
},
}
output, err := svc.DescribeAvailabilityZones(context.TODO(), input)
if err != nil {
return "", err
}
// Filter out the current availability zone
availableAZs := []string{}
for _, az := range output.AvailabilityZones {
if aws.ToString(az.ZoneName) != currentAZ {
availableAZs = append(availableAZs, aws.ToString(az.ZoneName))
}
}
// If no other availability zones are available, return an error
if len(availableAZs) == 0 {
return "", errors.New("no other availability zones available")
}
// Select a random availability zone from the remaining ones
randomIndex := rand.Intn(len(availableAZs))
randomAZ := availableAZs[randomIndex]
return randomAZ, nil
}
// terminateInstance kills an instance by id // terminateInstance kills an instance by id
func terminateInstance(svc *ec2.Client, instanceID string) error { func terminateInstance(svc *ec2.Client, instanceID string) error {
input := &ec2.TerminateInstancesInput{ input := &ec2.TerminateInstancesInput{