Skip to content

Commit

Permalink
elastic bean stalk application implementation (#644)
Browse files Browse the repository at this point in the history
* elastic bean stalk application implementation

* elastic bean stalk application implementation
  • Loading branch information
james03160927 authored Feb 28, 2024
1 parent 250f467 commit e9f66f6
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ Cloud-nuke suppports 🔎 inspecting and 🔥💀 deleting the following AWS res
| Elasticache | Clusters |
| Elasticache | Parameter Groups |
| Elasticache | Subnet Groups |
| Elastic Beanstalk | Applications |
| ECS | Services |
| ECS | Clusters |
| EKS | Clusters |
Expand Down Expand Up @@ -509,6 +510,7 @@ of the file that are supported are listed here.
| rds-subnet-group | DBSubnetGroups | ✅ (DB Subnet Group Name) | ❌ | ❌ |
| dynamodb | DynamoDB | ✅ (Table Name) | ✅ (Creation Time) | ❌ |
| ebs | EBSVolume | ✅ (Volume Name) | ✅ (Creation Time) | ✅ |
| elastic-beanstalk | ElasticBeanstalk | ✅ (Application Name) | ✅ (Creation Time) | ❌ |
| ec2 | EC2 | ✅ (Instance Name) | ✅ (Launch Time) | ✅ |
| ec2-dedicated-hosts | EC2DedicatedHosts | ✅ (EC2 Name Tag) | ✅ (Allocation Time) | ❌ |
| ec2-dhcp-option | EC2DhcpOption | ❌ | ❌ | ❌ |
Expand Down
1 change: 1 addition & 0 deletions aws/resource_registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ func getRegisteredRegionalResources() []AwsResource {
&resources.ConfigServiceRule{},
&resources.DynamoDB{},
&resources.EBSVolumes{},
&resources.EBApplications{},
&resources.EC2Instances{},
&resources.EC2DedicatedHosts{},
&resources.EC2KeyPairs{},
Expand Down
71 changes: 71 additions & 0 deletions aws/resources/elastic_beanstalk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package resources

import (
"context"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/cloud-nuke/logging"
"github.com/gruntwork-io/cloud-nuke/report"
"github.com/gruntwork-io/go-commons/errors"
)

func shouldIncludeEBApplication(app *elasticbeanstalk.ApplicationDescription, configObj config.Config) bool {
return configObj.ElasticBeanstalk.ShouldInclude(config.ResourceValue{
Name: app.ApplicationName,
Time: app.DateCreated,
})
}

// Returns a formatted string of EB application ids
func (eb *EBApplications) getAll(c context.Context, configObj config.Config) ([]*string, error) {
output, err := eb.Client.DescribeApplications(&elasticbeanstalk.DescribeApplicationsInput{})
if err != nil {
return nil, errors.WithStackTrace(err)
}
var appIds []*string
for _, app := range output.Applications {
if shouldIncludeEBApplication(app, configObj) {
appIds = append(appIds, app.ApplicationName)
}
}
return appIds, nil
}

// Deletes all EB Applications
func (eb *EBApplications) nukeAll(appIds []*string) error {
if len(appIds) == 0 {
logging.Debugf("No Elastic Beanstalk to nuke in region %s", eb.Region)
return nil
}

logging.Debugf("Deleting all Elastic Beanstalk applications in region %s", eb.Region)
var deletedApps []*string

for _, id := range appIds {
_, err := eb.Client.DeleteApplication(&elasticbeanstalk.DeleteApplicationInput{
ApplicationName: id,
TerminateEnvByForce: aws.Bool(true),
})
// Record status of this resource
e := report.Entry{
Identifier: aws.StringValue(id),
ResourceType: "Elastic Beanstalk Application",
Error: err,
}
report.Record(e)

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

// get the deleted ids
deletedApps = append(deletedApps, id)
logging.Debugf("Deleted Elastic Beanstalk application: %s", *id)

}
logging.Debugf("[OK] %d Elastic Beanstalk application(s) deleted in %s", len(deletedApps), eb.Region)
return nil
}
99 changes: 99 additions & 0 deletions aws/resources/elastic_beanstalk_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package resources

import (
"context"
"regexp"
"testing"
"time"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
"github.com/aws/aws-sdk-go/service/elasticbeanstalk/elasticbeanstalkiface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/stretchr/testify/require"
)

type mockedEBApplication struct {
elasticbeanstalkiface.ElasticBeanstalkAPI
DescribeApplicationsOutput elasticbeanstalk.DescribeApplicationsOutput
}

func (mock *mockedEBApplication) DescribeApplications(req *elasticbeanstalk.DescribeApplicationsInput) (*elasticbeanstalk.DescribeApplicationsOutput, error) {
return &mock.DescribeApplicationsOutput, nil
}

func (mock *mockedEBApplication) DeleteApplication(*elasticbeanstalk.DeleteApplicationInput) (*elasticbeanstalk.DeleteApplicationOutput, error) {
return nil, nil
}

func TestEBApplication_GetAll(t *testing.T) {
t.Parallel()

app1 := "demo-app-golang-backend"
app2 := "demo-app-golang-frontend"

now := time.Now()
eb := EBApplications{
Client: &mockedEBApplication{
DescribeApplicationsOutput: elasticbeanstalk.DescribeApplicationsOutput{
Applications: []*elasticbeanstalk.ApplicationDescription{
{
ApplicationArn: aws.String("app-arn-01"),
ApplicationName: &app1,
DateCreated: aws.Time(now),
},
{
ApplicationArn: aws.String("app-arn-02"),
ApplicationName: &app2,
DateCreated: aws.Time(now.Add(1)),
},
},
},
},
}

tests := map[string]struct {
configObj config.ResourceType
expected []string
}{
"emptyFilter": {
configObj: config.ResourceType{},
expected: []string{
app1, app2,
},
},
"nameExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
NamesRegExp: []config.Expression{{
RE: *regexp.MustCompile(app1),
}}},
},
expected: []string{app2},
},
"timeAfterExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
TimeAfter: aws.Time(now),
}},
expected: []string{app1},
},
"timeBeforeExclusionFilter": {
configObj: config.ResourceType{
ExcludeRule: config.FilterRule{
TimeBefore: aws.Time(now.Add(1)),
}},
expected: []string{app2},
},
}

for name, tc := range tests {
t.Run(name, func(t *testing.T) {
names, err := eb.getAll(context.Background(), config.Config{
ElasticBeanstalk: tc.configObj,
})
require.NoError(t, err)
require.Equal(t, tc.expected, aws.StringValueSlice(names))
})
}
}
58 changes: 58 additions & 0 deletions aws/resources/elastic_beanstalk_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package resources

import (
"context"

awsgo "github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/elasticbeanstalk"
"github.com/aws/aws-sdk-go/service/elasticbeanstalk/elasticbeanstalkiface"
"github.com/gruntwork-io/cloud-nuke/config"
"github.com/gruntwork-io/go-commons/errors"
)

// EBApplications - represents all elastic beanstalk applications
type EBApplications struct {
BaseAwsResource
Client elasticbeanstalkiface.ElasticBeanstalkAPI
Region string
appIds []string
}

func (eb *EBApplications) Init(session *session.Session) {
eb.Client = elasticbeanstalk.New(session)
}

// ResourceName - the simple name of the aws resource
func (eb *EBApplications) ResourceName() string {
return "elastic-beanstalk"
}

// ResourceIdentifiers - The application ids of the elastic beanstalk
func (eb *EBApplications) ResourceIdentifiers() []string {
return eb.appIds
}

func (eb *EBApplications) MaxBatchSize() int {
// Tentative batch size to ensure AWS doesn't throttle
return 49
}

func (eb *EBApplications) GetAndSetIdentifiers(c context.Context, configObj config.Config) ([]string, error) {
identifiers, err := eb.getAll(c, configObj)
if err != nil {
return nil, err
}

eb.appIds = awsgo.StringValueSlice(identifiers)
return eb.appIds, nil
}

// Nuke - nuke 'em all!!!
func (eb *EBApplications) Nuke(identifiers []string) error {
if err := eb.nukeAll(awsgo.StringSlice(identifiers)); err != nil {
return errors.WithStackTrace(err)
}

return nil
}
1 change: 1 addition & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ type Config struct {
DBSubnetGroups ResourceType `yaml:"DBSubnetGroups"`
DynamoDB ResourceType `yaml:"DynamoDB"`
EBSVolume ResourceType `yaml:"EBSVolume"`
ElasticBeanstalk ResourceType `yaml:"ElasticBeanstalk"`
EC2 ResourceType `yaml:"EC2"`
EC2DedicatedHosts ResourceType `yaml:"EC2DedicatedHosts"`
EC2DHCPOption ResourceType `yaml:"EC2DhcpOption"`
Expand Down
1 change: 1 addition & 0 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ func emptyConfig() *Config {
ResourceType{FilterRule{}, FilterRule{}},
ResourceType{FilterRule{}, FilterRule{}},
ResourceType{FilterRule{}, FilterRule{}},
ResourceType{FilterRule{}, FilterRule{}},
KMSCustomerKeyResourceType{false, ResourceType{FilterRule{}, FilterRule{}}},
ResourceType{FilterRule{}, FilterRule{}},
ResourceType{FilterRule{}, FilterRule{}},
Expand Down

0 comments on commit e9f66f6

Please sign in to comment.