resources/ec2-image.go
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(®istry.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
}