github.com/olli-ai/jx/v2@v2.0.400-0.20210921045218-14731b4dd448/pkg/cloud/amazon/ec2/ec2.go (about) 1 package ec2 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/aws/aws-sdk-go/aws/client" 8 9 "github.com/aws/aws-sdk-go/service/ec2" 10 11 "github.com/jenkins-x/jx-logging/pkg/log" 12 "github.com/olli-ai/jx/v2/pkg/cluster" 13 14 "github.com/aws/aws-sdk-go/aws" 15 16 "github.com/aws/aws-sdk-go/service/ec2/ec2iface" 17 18 "github.com/pkg/errors" 19 ) 20 21 // ec2APIHandler contains some functions to interact with and serves as an abstraction of the EC2 API. 22 type ec2APIHandler struct { 23 ec2 ec2iface.EC2API 24 } 25 26 // NewEC2APIHandler will return an ec2APIHandler with configured credentials 27 func NewEC2APIHandler(awsSession client.ConfigProvider, ec2api ...ec2iface.EC2API) (*ec2APIHandler, error) { 28 if len(ec2api) == 1 { 29 return &ec2APIHandler{ 30 ec2: ec2api[0], 31 }, nil 32 } 33 return &ec2APIHandler{ 34 ec2: ec2.New(awsSession), 35 }, nil 36 } 37 38 // DeleteVolumesForCluster should delete every volume with Kubernetes / JX owned tags 39 func (e ec2APIHandler) DeleteVolumesForCluster(cluster *cluster.Cluster) error { 40 log.Logger().Infof("Attempting to delete jx EC2 Volumes for cluster %s", cluster.Name) 41 output, err := e.ec2.DescribeVolumes(&ec2.DescribeVolumesInput{ 42 Filters: []*ec2.Filter{ 43 { 44 Name: aws.String(fmt.Sprintf("tag:kubernetes.io/cluster/%s", cluster.Name)), 45 Values: []*string{aws.String("owned")}, 46 }, 47 { 48 Name: aws.String("tag:kubernetes.io/created-for/pvc/namespace"), 49 Values: []*string{aws.String("jx")}, 50 }, 51 }, 52 }) 53 if err != nil { 54 return errors.Wrapf(err, "error describing volumes for cluster %s", cluster.Name) 55 } 56 57 for _, v := range output.Volumes { 58 log.Logger().Debugf("Deleting EC2 Volume %s for cluster %s", *v.VolumeId, cluster.Name) 59 var attachments []*string 60 for _, i := range v.Attachments { 61 if *i.State == ec2.VolumeAttachmentStateAttached { 62 attachments = append(attachments, i.InstanceId) 63 } 64 } 65 if *v.State == ec2.VolumeStateInUse { 66 log.Logger().Debugf("Terminating instances before deleting volumes: %s", strings.Join(aws.StringValueSlice(attachments), ",")) 67 _, err := e.ec2.TerminateInstances(&ec2.TerminateInstancesInput{ 68 InstanceIds: attachments, 69 }) 70 if err != nil { 71 return errors.Wrapf(err, "error terminating EC2 instances %s", 72 strings.Join(aws.StringValueSlice(attachments), ",")) 73 } 74 log.Logger().Info("Waiting until EC2 instances are terminated") 75 err = e.ec2.WaitUntilInstanceTerminated(&ec2.DescribeInstancesInput{ 76 InstanceIds: attachments, 77 }) 78 if err != nil { 79 return errors.Wrap(err, "error waiting until EC2 instances are terminated") 80 } 81 82 log.Logger().Info("Waiting until Volumes are in an Available state before deleting") 83 err = e.ec2.WaitUntilVolumeAvailable(&ec2.DescribeVolumesInput{ 84 VolumeIds: []*string{v.VolumeId}, 85 }) 86 if err != nil { 87 return errors.Wrap(err, "error waiting until volume is detached") 88 } 89 } 90 log.Logger().Infof("Deleting volume %s", *v.VolumeId) 91 _, err = e.ec2.DeleteVolume(&ec2.DeleteVolumeInput{ 92 VolumeId: v.VolumeId, 93 }) 94 if err != nil { 95 return errors.Wrapf(err, "error deleting EC2 Volume %s", *v.VolumeId) 96 } 97 } 98 return nil 99 }