2023-03-28 16:09:14 +02:00
|
|
|
package mtdaws
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2023-04-17 15:20:40 +02:00
|
|
|
"net/netip"
|
2023-04-11 12:56:07 +02:00
|
|
|
"time"
|
2023-03-28 16:09:14 +02:00
|
|
|
|
2023-04-12 10:16:22 +02:00
|
|
|
"github.com/aws/aws-sdk-go-v2/aws"
|
2023-03-28 16:09:14 +02:00
|
|
|
"github.com/aws/aws-sdk-go-v2/service/ec2"
|
2023-04-18 11:08:31 +02:00
|
|
|
"github.com/aws/aws-sdk-go-v2/service/ec2/types"
|
2023-04-17 15:54:42 +02:00
|
|
|
"github.com/google/uuid"
|
2023-04-25 15:52:34 +02:00
|
|
|
"github.com/thefeli73/polemos/pcsdk"
|
2023-03-28 16:09:14 +02:00
|
|
|
"github.com/thefeli73/polemos/state"
|
|
|
|
)
|
|
|
|
|
|
|
|
// AWSMoveInstance moves a specified instance to a new availability region
|
|
|
|
func AWSMoveInstance(config state.Config) (state.Config) {
|
2023-04-17 15:54:42 +02:00
|
|
|
|
2023-04-12 15:31:11 +02:00
|
|
|
// pseudorandom instance from all services for testing
|
|
|
|
var serviceUUID state.CustomUUID
|
|
|
|
var instance state.Service
|
|
|
|
for key, service := range config.MTD.Services {
|
|
|
|
serviceUUID = key
|
|
|
|
instance = service
|
2023-04-19 11:39:11 +02:00
|
|
|
if !instance.AdminEnabled {continue}
|
|
|
|
if !instance.Active {continue}
|
2023-04-12 15:31:11 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
|
2023-04-17 15:54:42 +02:00
|
|
|
fmt.Println("MTD move service:\t", uuid.UUID.String(uuid.UUID(serviceUUID)))
|
2023-04-12 15:31:11 +02:00
|
|
|
|
2023-04-26 14:20:56 +02:00
|
|
|
// Test Proxy Connection
|
|
|
|
t := time.Now()
|
|
|
|
s := pcsdk.NewCommandStatus()
|
|
|
|
err := s.Execute(netip.AddrPortFrom(instance.EntryIP, config.MTD.ManagementPort))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("error executing test command: %s\n", err)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
fmt.Printf("Proxy Tested. (took %s)\n", time.Since(t).Round(100*time.Millisecond).String())
|
2023-03-28 16:09:14 +02:00
|
|
|
region, instanceID := DecodeCloudID(instance.CloudID)
|
|
|
|
awsConfig := NewConfig(region, config.AWS.CredentialsPath)
|
|
|
|
svc := ec2.NewFromConfig(awsConfig)
|
|
|
|
|
|
|
|
realInstance, err := getInstanceDetailsFromString(svc, instanceID)
|
|
|
|
if err != nil {
|
2023-04-12 10:16:22 +02:00
|
|
|
fmt.Println("Error getting instance details:\t", err)
|
2023-03-28 16:09:14 +02:00
|
|
|
return config
|
|
|
|
}
|
2023-04-18 15:03:34 +02:00
|
|
|
if !isInstanceRunning(realInstance) {
|
|
|
|
fmt.Println("Error, Instance is not running!")
|
2023-04-18 11:08:31 +02:00
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
2023-04-19 11:39:11 +02:00
|
|
|
//Create image
|
2023-04-26 14:20:56 +02:00
|
|
|
t = time.Now()
|
2023-03-28 16:09:14 +02:00
|
|
|
imageName, err := createImage(svc, instanceID)
|
|
|
|
if err != nil {
|
2023-04-12 10:16:22 +02:00
|
|
|
fmt.Println("Error creating image:\t", err)
|
2023-03-28 16:09:14 +02:00
|
|
|
return config
|
|
|
|
}
|
2023-04-19 11:39:11 +02:00
|
|
|
fmt.Printf("Created image:\t\t%s (took %s)\n", imageName, time.Since(t).Round(100*time.Millisecond).String())
|
2023-03-28 16:09:14 +02:00
|
|
|
|
2023-04-19 11:39:11 +02:00
|
|
|
// Wait for image
|
|
|
|
t = time.Now()
|
2023-04-11 12:56:07 +02:00
|
|
|
err = waitForImageReady(svc, imageName, 5*time.Minute)
|
|
|
|
if err != nil {
|
2023-04-12 10:16:22 +02:00
|
|
|
fmt.Println("Error waiting for image to be ready:\t", err)
|
2023-04-11 12:56:07 +02:00
|
|
|
return config
|
|
|
|
}
|
2023-04-19 11:39:11 +02:00
|
|
|
fmt.Printf("Image is ready:\t\t%s (took %s)\n", imageName, time.Since(t).Round(100*time.Millisecond).String())
|
2023-04-11 12:56:07 +02:00
|
|
|
|
2023-04-19 11:39:11 +02:00
|
|
|
// Launch new instance
|
|
|
|
t = time.Now()
|
2023-04-17 15:54:42 +02:00
|
|
|
newInstanceID, err := launchInstance(svc, realInstance, imageName, region)
|
2023-03-28 16:09:14 +02:00
|
|
|
if err != nil {
|
2023-04-12 10:16:22 +02:00
|
|
|
fmt.Println("Error launching instance:\t", err)
|
2023-03-28 16:09:14 +02:00
|
|
|
return config
|
|
|
|
}
|
2023-04-19 11:39:11 +02:00
|
|
|
fmt.Printf("Launched new instance:\t%s (took %s)\n", newInstanceID, time.Since(t).Round(100*time.Millisecond).String())
|
2023-03-28 16:09:14 +02:00
|
|
|
|
2023-04-25 15:52:34 +02:00
|
|
|
// Wait for instance
|
|
|
|
t = time.Now()
|
|
|
|
err = waitForInstanceReady(svc, newInstanceID, 5*time.Minute)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error waiting for instance to be ready:\t", err)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
fmt.Printf("instance is ready:\t\t%s (took %s)\n", newInstanceID, time.Since(t).Round(100*time.Millisecond).String())
|
|
|
|
|
|
|
|
// Reconfigure Proxy to new instance
|
2023-04-19 11:39:11 +02:00
|
|
|
t = time.Now()
|
2023-04-25 15:52:34 +02:00
|
|
|
m := pcsdk.NewCommandModify(instance.ServicePort, instance.ServiceIP, serviceUUID)
|
|
|
|
err = m.Execute(netip.AddrPortFrom(instance.EntryIP, config.MTD.ManagementPort))
|
|
|
|
if err != nil {
|
|
|
|
fmt.Printf("error executing modify command: %s\n", err)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
fmt.Printf("Proxy modified. (took %s)\n", time.Since(t).Round(100*time.Millisecond).String())
|
|
|
|
|
|
|
|
|
|
|
|
AWSUpdateService(config, region, serviceUUID, newInstanceID)
|
|
|
|
|
|
|
|
cleanupAWS(svc, config, instanceID, imageName)
|
|
|
|
|
|
|
|
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
// AWSUpdateService updates a specified service config to match a newly moved instance
|
|
|
|
func AWSUpdateService(config state.Config, region string, service state.CustomUUID, newInstanceID string) (state.Config) {
|
|
|
|
awsConfig := NewConfig(region, config.AWS.CredentialsPath)
|
|
|
|
svc := ec2.NewFromConfig(awsConfig)
|
|
|
|
instance, err := getInstanceDetailsFromString(svc, newInstanceID)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error getting instance details:\t", err)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
var publicAddr string
|
|
|
|
if instance.PublicIpAddress != nil {
|
|
|
|
publicAddr = aws.ToString(instance.PublicIpAddress)
|
|
|
|
}
|
|
|
|
formattedinstance := AwsInstance{
|
|
|
|
InstanceID: aws.ToString(instance.InstanceId),
|
|
|
|
Region: region,
|
|
|
|
PublicIP: publicAddr,
|
|
|
|
PrivateIP: aws.ToString(instance.PrivateIpAddress),
|
|
|
|
}
|
|
|
|
cloudid := GetCloudID(formattedinstance)
|
|
|
|
serviceip := netip.MustParseAddr(publicAddr)
|
|
|
|
config.MTD.Services[service] = state.Service{CloudID: cloudid, ServiceIP: serviceip}
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
|
|
|
|
// isInstanceRunning returns if an instance is running (true=running)
|
|
|
|
func isInstanceRunning(instance *types.Instance) bool {
|
|
|
|
return instance.State.Name == types.InstanceStateNameRunning
|
|
|
|
}
|
|
|
|
|
|
|
|
// cleanupAWS terminates the old instance, deregisters the image and deletes the old snapshot
|
|
|
|
func cleanupAWS(svc *ec2.Client, config state.Config, instanceID string, imageName string) state.Config {
|
|
|
|
// Terminate old instance
|
|
|
|
t := time.Now()
|
|
|
|
err := terminateInstance(svc, instanceID)
|
2023-03-28 16:09:14 +02:00
|
|
|
if err != nil {
|
2023-04-12 10:16:22 +02:00
|
|
|
fmt.Println("Error terminating instance:\t", err)
|
2023-03-28 16:09:14 +02:00
|
|
|
return config
|
|
|
|
}
|
2023-04-19 11:39:11 +02:00
|
|
|
fmt.Printf("Killed old instance:\t%s (took %s)\n", instanceID, time.Since(t).Round(100*time.Millisecond).String())
|
2023-03-28 16:09:14 +02:00
|
|
|
|
2023-04-19 11:39:11 +02:00
|
|
|
// Deregister old image
|
|
|
|
t = time.Now()
|
2023-04-12 10:16:22 +02:00
|
|
|
image, err := describeImage(svc, imageName)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error describing image:\t", err)
|
|
|
|
return config
|
|
|
|
}
|
|
|
|
err = deregisterImage(svc, imageName)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error deregistering image:\t", err)
|
|
|
|
return config
|
|
|
|
}
|
2023-04-19 11:39:11 +02:00
|
|
|
fmt.Printf("Deregistered image:\t%s (took %s)\n", imageName, time.Since(t).Round(100*time.Millisecond).String())
|
2023-04-12 10:16:22 +02:00
|
|
|
|
2023-04-19 11:39:11 +02:00
|
|
|
// Delete old snapshot
|
|
|
|
t = time.Now()
|
2023-04-12 10:16:22 +02:00
|
|
|
if len(image.BlockDeviceMappings) > 0 {
|
|
|
|
snapshotID := aws.ToString(image.BlockDeviceMappings[0].Ebs.SnapshotId)
|
|
|
|
err = deleteSnapshot(svc, snapshotID)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("Error deleting snapshot:\t", err)
|
|
|
|
return config
|
|
|
|
}
|
2023-04-19 11:39:11 +02:00
|
|
|
fmt.Printf("Deleted snapshot:\t%s (took %s)\n", snapshotID, time.Since(t).Round(100*time.Millisecond).String())
|
2023-04-12 10:16:22 +02:00
|
|
|
}
|
2023-03-28 16:09:14 +02:00
|
|
|
return config
|
|
|
|
}
|