Skip to content

Commit

Permalink
Add nil pointer checks to avoid exception for VPC nuke
Browse files Browse the repository at this point in the history
  • Loading branch information
james03160927 committed Dec 12, 2024
1 parent 8ceb3d3 commit 24acb1a
Showing 1 changed file with 74 additions and 12 deletions.
86 changes: 74 additions & 12 deletions aws/resources/ec2.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,35 @@ import (
// returns only instance Ids of unprotected ec2 instances
func (ei *EC2Instances) filterOutProtectedInstances(output *ec2.DescribeInstancesOutput, configObj config.Config) ([]*string, error) {
var filteredIds []*string
if output == nil {
logging.Debugf("DescribeInstancesOutput is nil")
return filteredIds, nil
}

if output.Reservations == nil {
logging.Debugf("No reservations found in DescribeInstancesOutput")
return filteredIds, nil
}

for _, reservation := range output.Reservations {
if reservation == nil {
logging.Debugf("Encountered a nil reservation")
continue
}
if reservation.Instances == nil {
logging.Debugf("No instances found in reservation")
continue
}

for _, instance := range reservation.Instances {
if instance == nil {
logging.Debugf("Encountered a nil instance in reservation")
continue
}
if instance.InstanceId == nil {
logging.Debugf("InstanceId is nil for an instance")
continue
}
instanceID := *instance.InstanceId

attr, err := ei.Client.DescribeInstanceAttributeWithContext(ei.Context, &ec2.DescribeInstanceAttributeInput{
Expand All @@ -26,6 +53,11 @@ func (ei *EC2Instances) filterOutProtectedInstances(output *ec2.DescribeInstance
return nil, errors.WithStackTrace(err)
}

if attr == nil || attr.DisableApiTermination == nil || attr.DisableApiTermination.Value == nil {
logging.Debugf("Missing or nil disableApiTermination attribute for instance: %s", instanceID)
continue
}

if shouldIncludeInstanceId(instance, *attr.DisableApiTermination.Value, configObj) {
filteredIds = append(filteredIds, &instanceID)
}
Expand Down Expand Up @@ -63,13 +95,25 @@ func (ei *EC2Instances) getAll(c context.Context, configObj config.Config) ([]*s
}

func shouldIncludeInstanceId(instance *ec2.Instance, protected bool, configObj config.Config) bool {
if instance == nil {
logging.Debugf("Encountered a nil instance")
return false
}
if protected {
logging.Debugf("Instance is protected: %s", awsgo.StringValue(instance.InstanceId))
return false
}

// If Name is unset, GetEC2ResourceNameTagValue returns error and zero value string
// Ignore this error and pass empty string to config.ShouldInclude
instanceName := util.GetEC2ResourceNameTagValue(instance.Tags)
var instanceName *string
if instance.Tags != nil {
instanceName = util.GetEC2ResourceNameTagValue(instance.Tags)
if instanceName == nil {
logging.Debugf("Name tag is nil for instance %s", awsgo.StringValue(instance.InstanceId))
}
} else {
logging.Debugf("Instance %s has no tags", awsgo.StringValue(instance.InstanceId))
}

return configObj.EC2.ShouldInclude(config.ResourceValue{
Name: instanceName,
Time: instance.LaunchTime,
Expand All @@ -78,10 +122,18 @@ func shouldIncludeInstanceId(instance *ec2.Instance, protected bool, configObj c
}

func (ei *EC2Instances) releaseEIPs(instanceIds []*string) error {
if instanceIds == nil {
logging.Debugf("InstanceIds is nil")
return nil
}

logging.Debugf("Releasing Elastic IP address(s) associated on instances")
for _, instanceID := range instanceIds {
if instanceID == nil {
logging.Debugf("Encountered a nil instance ID while releasing EIPs")
continue
}

// get the elastic ip's associated with the EC2's
output, err := ei.Client.DescribeAddressesWithContext(ei.Context, &ec2.DescribeAddressesInput{
Filters: []*ec2.Filter{
{
Expand All @@ -96,13 +148,21 @@ func (ei *EC2Instances) releaseEIPs(instanceIds []*string) error {
return err
}

if output == nil || output.Addresses == nil {
logging.Debugf("No addresses found for instance: %s", *instanceID)
continue
}
for _, address := range output.Addresses {
if address == nil || address.AllocationId == nil {
logging.Debugf("Encountered a nil address or AllocationId for instance: %s", *instanceID)
continue
}
_, err := ei.Client.ReleaseAddressWithContext(ei.Context, &ec2.ReleaseAddressInput{
AllocationId: address.AllocationId,
})

if err != nil {
logging.Debugf("An error happened while releasing the elastic ip address %s, error %v", *address.AllocationId, err)
logging.Debugf("Error releasing Elastic IP address %s: %v", *address.AllocationId, err)
continue
}
logging.Debugf("Released Elastic IP address %s from instance %s", *address.AllocationId, *instanceID)
Expand All @@ -112,15 +172,13 @@ func (ei *EC2Instances) releaseEIPs(instanceIds []*string) error {
return nil
}

// Deletes all non protected EC2 instances
// Deletes all non-protected EC2 instances
func (ei *EC2Instances) nukeAll(instanceIds []*string) error {
if len(instanceIds) == 0 {
logging.Debugf("No EC2 instances to nuke in region %s", ei.Region)
return nil
}

// release the attached elastic ip's
// Note: This should be done before terminating the EC2 instances
err := ei.releaseEIPs(instanceIds)
if err != nil {
logging.Debugf("[Failed EIP release] %s", err)
Expand All @@ -147,15 +205,19 @@ func (ei *EC2Instances) nukeAll(instanceIds []*string) error {
},
},
})
for _, instanceID := range instanceIds {
logging.Debugf("Terminated EC2 Instance: %s", *instanceID)
}

if err != nil {
logging.Debugf("[Failed] %s", err)
return errors.WithStackTrace(err)
}

for _, instanceID := range instanceIds {
if instanceID != nil {
logging.Debugf("Terminated EC2 Instance: %s", *instanceID)
} else {
logging.Debugf("Encountered a nil instance ID in the list of terminated instances")
}
}

logging.Debugf("[OK] %d instance(s) terminated in %s", len(instanceIds), ei.Region)
return nil
}

0 comments on commit 24acb1a

Please sign in to comment.