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  }