ekristen/aws-nuke

View on GitHub
resources/ec2-image.go

Summary

Maintainability
A
0 mins
Test Coverage
package resources

import (
    "context"
    "fmt"
    "strings"

    "github.com/gotidy/ptr"

    "github.com/aws/aws-sdk-go/aws"
    "github.com/aws/aws-sdk-go/service/ec2"

    "github.com/ekristen/libnuke/pkg/registry"
    "github.com/ekristen/libnuke/pkg/resource"
    libsettings "github.com/ekristen/libnuke/pkg/settings"
    "github.com/ekristen/libnuke/pkg/types"

    "github.com/ekristen/aws-nuke/v3/pkg/nuke"
)

const EC2ImageResource = "EC2Image"

const IncludeDeprecatedSetting = "IncludeDeprecated"
const IncludeDisabledSetting = "IncludeDisabled"
const DisableDeregistrationProtectionSetting = "DisableDeregistrationProtection"

func init() {
    registry.Register(&registry.Registration{
        Name:     EC2ImageResource,
        Scope:    nuke.Account,
        Resource: &EC2Image{},
        Lister:   &EC2ImageLister{},
        Settings: []string{
            DisableDeregistrationProtectionSetting,
            IncludeDeprecatedSetting,
            IncludeDisabledSetting,
        },
    })
}

type EC2ImageLister struct{}

func (l *EC2ImageLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) {
    resources := make([]resource.Resource, 0)
    opts := o.(*nuke.ListerOpts)

    svc := ec2.New(opts.Session)
    params := &ec2.DescribeImagesInput{
        Owners: []*string{
            aws.String("self"),
        },
        IncludeDeprecated: ptr.Bool(true),
        IncludeDisabled:   ptr.Bool(true),
    }
    resp, err := svc.DescribeImages(params)
    if err != nil {
        return nil, err
    }

    for _, out := range resp.Images {
        visibility := "Private"
        if ptr.ToBool(out.Public) {
            visibility = "Public"
        }

        resources = append(resources, &EC2Image{
            svc:                      svc,
            id:                       out.ImageId,
            name:                     out.Name,
            tags:                     out.Tags,
            state:                    out.State,
            visibility:               ptr.String(visibility),
            creationDate:             out.CreationDate,
            deprecated:               ptr.Bool(out.DeprecationTime != nil),
            deprecatedTime:           out.DeprecationTime,
            deregistrationProtection: out.DeregistrationProtection,
        })
    }

    return resources, nil
}

type EC2Image struct {
    svc      *ec2.EC2
    settings *libsettings.Setting

    id                       *string
    name                     *string
    tags                     []*ec2.Tag
    state                    *string
    visibility               *string
    deprecated               *bool
    deprecatedTime           *string
    creationDate             *string
    deregistrationProtection *string
}

func (e *EC2Image) Filter() error {
    if ptr.ToString(e.state) == "pending" {
        return fmt.Errorf("ineligible state for removal")
    }

    if strings.HasPrefix(*e.deregistrationProtection, "disabled after") {
        return fmt.Errorf("would remove after %s due to deregistration protection cooldown",
            strings.ReplaceAll(*e.deregistrationProtection, "disabled after ", ""))
    }

    if ptr.ToString(e.deregistrationProtection) != ec2.ImageStateDisabled &&
        !e.settings.GetBool(DisableDeregistrationProtectionSetting) {
        return fmt.Errorf("deregistration protection is enabled")
    }

    // TODO(v4): enable by default
    if !e.settings.GetBool(IncludeDeprecatedSetting) && ptr.ToBool(e.deprecated) {
        return fmt.Errorf("excluded by %s setting being false", IncludeDeprecatedSetting)
    }

    // TODO(v4): enable by default
    if !e.settings.GetBool(IncludeDisabledSetting) && ptr.ToString(e.state) == ec2.ImageStateDisabled {
        return fmt.Errorf("excluded by %s setting being false", IncludeDisabledSetting)
    }

    return nil
}

func (e *EC2Image) Remove(_ context.Context) error {
    if err := e.removeDeregistrationProtection(); err != nil {
        return err
    }

    if *e.deregistrationProtection == "enabled-with-cooldown" {
        return nil
    }

    _, err := e.svc.DeregisterImage(&ec2.DeregisterImageInput{
        ImageId: e.id,
    })

    return err
}

func (e *EC2Image) removeDeregistrationProtection() error {
    if *e.deregistrationProtection == ec2.ImageStateDisabled {
        return nil
    }

    if !e.settings.GetBool(DisableDeregistrationProtectionSetting) {
        return nil
    }

    _, err := e.svc.DisableImageDeregistrationProtection(&ec2.DisableImageDeregistrationProtectionInput{
        ImageId: e.id,
    })
    return err
}

func (e *EC2Image) Properties() types.Properties {
    properties := types.NewProperties()

    properties.Set("CreationDate", e.creationDate)
    properties.Set("Name", e.name)
    properties.Set("State", e.state)
    properties.Set("Visibility", e.visibility)
    properties.Set("Deprecated", e.deprecated)
    properties.Set("DeprecatedTime", e.deprecatedTime)
    properties.Set("DeregistrationProtection", e.deregistrationProtection)

    for _, tagValue := range e.tags {
        properties.SetTag(tagValue.Key, tagValue.Value)
    }
    return properties
}

func (e *EC2Image) String() string {
    return *e.id
}

func (e *EC2Image) Settings(settings *libsettings.Setting) {
    e.settings = settings
}