diff --git a/resources/athena-data-catalog.go b/resources/athena-data-catalog.go new file mode 100644 index 00000000..f361f6e8 --- /dev/null +++ b/resources/athena-data-catalog.go @@ -0,0 +1,89 @@ +package resources + +import ( + "context" + "fmt" + + "github.com/aws/aws-sdk-go/aws" + + "github.com/aws/aws-sdk-go/service/athena" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const AthenaDataCatalogResource = "AthenaDataCatalog" + +func init() { + registry.Register(®istry.Registration{ + Name: AthenaDataCatalogResource, + Scope: nuke.Account, + Lister: &AthenaDataCatalogLister{}, + }) +} + +type AthenaDataCatalogLister struct{} + +func (l *AthenaDataCatalogLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := athena.New(opts.Session) + resources := make([]resource.Resource, 0) + + params := &athena.ListDataCatalogsInput{ + MaxResults: aws.Int64(50), + } + + for { + output, err := svc.ListDataCatalogs(params) + if err != nil { + return nil, err + } + + for _, catalog := range output.DataCatalogsSummary { + resources = append(resources, &AthenaDataCatalog{ + svc: svc, + Name: catalog.CatalogName, + }) + } + + if output.NextToken == nil { + break + } + + params.NextToken = output.NextToken + } + + return resources, nil +} + +type AthenaDataCatalog struct { + svc *athena.Athena + Name *string +} + +func (r *AthenaDataCatalog) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *AthenaDataCatalog) Remove(_ context.Context) error { + _, err := r.svc.DeleteDataCatalog(&athena.DeleteDataCatalogInput{ + Name: r.Name, + }) + + return err +} + +func (r *AthenaDataCatalog) Filter() error { + if *r.Name == "AwsDataCatalog" { + return fmt.Errorf("cannot delete default data source") + } + return nil +} + +func (r *AthenaDataCatalog) String() string { + return *r.Name +} diff --git a/resources/athena-named-queries.go b/resources/athena-named-query.go similarity index 86% rename from resources/athena-named-queries.go rename to resources/athena-named-query.go index b0b1a065..ed011f69 100644 --- a/resources/athena-named-queries.go +++ b/resources/athena-named-query.go @@ -24,11 +24,6 @@ func init() { type AthenaNamedQueryLister struct{} -type AthenaNamedQuery struct { - svc *athena.Athena - id *string -} - func (l *AthenaNamedQueryLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) @@ -76,19 +71,24 @@ func (l *AthenaNamedQueryLister) List(_ context.Context, o interface{}) ([]resou return resources, err } -func (a *AthenaNamedQuery) Remove(_ context.Context) error { - _, err := a.svc.DeleteNamedQuery(&athena.DeleteNamedQueryInput{ - NamedQueryId: a.id, +type AthenaNamedQuery struct { + svc *athena.Athena + id *string +} + +func (r *AthenaNamedQuery) Remove(_ context.Context) error { + _, err := r.svc.DeleteNamedQuery(&athena.DeleteNamedQueryInput{ + NamedQueryId: r.id, }) return err } -func (a *AthenaNamedQuery) Properties() types.Properties { +func (r *AthenaNamedQuery) Properties() types.Properties { return types.NewProperties(). - Set("Id", *a.id) + Set("Id", *r.id) } -func (a *AthenaNamedQuery) String() string { - return *a.id +func (r *AthenaNamedQuery) String() string { + return *r.id } diff --git a/resources/athena-prepared-statement.go b/resources/athena-prepared-statement.go new file mode 100644 index 00000000..4e591f57 --- /dev/null +++ b/resources/athena-prepared-statement.go @@ -0,0 +1,92 @@ +package resources + +import ( + "context" + + "github.com/aws/aws-sdk-go/aws" + + "github.com/aws/aws-sdk-go/service/athena" + + "github.com/ekristen/libnuke/pkg/registry" + "github.com/ekristen/libnuke/pkg/resource" + "github.com/ekristen/libnuke/pkg/types" + + "github.com/ekristen/aws-nuke/v3/pkg/nuke" +) + +const AthenaPreparedStatementResource = "AthenaPreparedStatement" + +func init() { + registry.Register(®istry.Registration{ + Name: AthenaPreparedStatementResource, + Scope: nuke.Account, + Lister: &AthenaPreparedStatementLister{}, + }) +} + +type AthenaPreparedStatementLister struct{} + +func (l *AthenaPreparedStatementLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { + opts := o.(*nuke.ListerOpts) + + svc := athena.New(opts.Session) + resources := make([]resource.Resource, 0) + + workgroups, err := svc.ListWorkGroups(&athena.ListWorkGroupsInput{}) + if err != nil { + return nil, err + } + + for _, workgroup := range workgroups.WorkGroups { + params := &athena.ListPreparedStatementsInput{ + WorkGroup: workgroup.Name, + MaxResults: aws.Int64(50), + } + + for { + output, err := svc.ListPreparedStatements(params) + if err != nil { + return nil, err + } + + for _, statement := range output.PreparedStatements { + resources = append(resources, &AthenaPreparedStatement{ + svc: svc, + Name: statement.StatementName, + WorkGroup: workgroup.Name, + }) + } + + if output.NextToken == nil { + break + } + + params.NextToken = output.NextToken + } + } + + return resources, nil +} + +type AthenaPreparedStatement struct { + svc *athena.Athena + Name *string + WorkGroup *string +} + +func (r *AthenaPreparedStatement) Properties() types.Properties { + return types.NewPropertiesFromStruct(r) +} + +func (r *AthenaPreparedStatement) Remove(_ context.Context) error { + _, err := r.svc.DeletePreparedStatement(&athena.DeletePreparedStatementInput{ + StatementName: r.Name, + WorkGroup: r.WorkGroup, + }) + + return err +} + +func (r *AthenaPreparedStatement) String() string { + return *r.Name +} diff --git a/resources/athena-work-groups.go b/resources/athena-work-group.go similarity index 80% rename from resources/athena-work-groups.go rename to resources/athena-work-group.go index 58c5675d..6b275e79 100644 --- a/resources/athena-work-groups.go +++ b/resources/athena-work-group.go @@ -31,12 +31,6 @@ func init() { type AthenaWorkGroupLister struct{} -type AthenaWorkGroup struct { - svc *athena.Athena - name *string - arn *string -} - func (l *AthenaWorkGroupLister) List(_ context.Context, o interface{}) ([]resource.Resource, error) { opts := o.(*nuke.ListerOpts) @@ -84,15 +78,21 @@ func (l *AthenaWorkGroupLister) List(_ context.Context, o interface{}) ([]resour return resources, err } -func (a *AthenaWorkGroup) Remove(_ context.Context) error { +type AthenaWorkGroup struct { + svc *athena.Athena + name *string + arn *string +} + +func (r *AthenaWorkGroup) Remove(_ context.Context) error { // Primary WorkGroup cannot be deleted, - // but we can reset it to a clean state - if *a.name == "primary" { + // but we can reset it to r clean state + if *r.name == "primary" { // TODO: pass logger via ListerOpts instead of using global logrus.Info("Primary Athena WorkGroup may not be deleted. Resetting configuration only.") // Reset the configuration to its default state - _, err := a.svc.UpdateWorkGroup(&athena.UpdateWorkGroupInput{ + _, err := r.svc.UpdateWorkGroup(&athena.UpdateWorkGroupInput{ // See https://docs.aws.amazon.com/athena/latest/APIReference/API_WorkGroupConfigurationUpdates.html // for documented defaults ConfigurationUpdates: &athena.WorkGroupConfigurationUpdates{ @@ -106,15 +106,15 @@ func (a *AthenaWorkGroup) Remove(_ context.Context) error { }, }, Description: aws.String(""), - WorkGroup: a.name, + WorkGroup: r.name, }) if err != nil { return err } // Remove any tags - wgTagsRes, err := a.svc.ListTagsForResource(&athena.ListTagsForResourceInput{ - ResourceARN: a.arn, + wgTagsRes, err := r.svc.ListTagsForResource(&athena.ListTagsForResourceInput{ + ResourceARN: r.arn, }) if err != nil { return err @@ -125,8 +125,8 @@ func (a *AthenaWorkGroup) Remove(_ context.Context) error { tagKeys = append(tagKeys, tag.Key) } - _, err = a.svc.UntagResource(&athena.UntagResourceInput{ - ResourceARN: a.arn, + _, err = r.svc.UntagResource(&athena.UntagResourceInput{ + ResourceARN: r.arn, TagKeys: tagKeys, }) if err != nil { @@ -136,35 +136,35 @@ func (a *AthenaWorkGroup) Remove(_ context.Context) error { return nil } - _, err := a.svc.DeleteWorkGroup(&athena.DeleteWorkGroupInput{ + _, err := r.svc.DeleteWorkGroup(&athena.DeleteWorkGroupInput{ RecursiveDeleteOption: aws.Bool(true), - WorkGroup: a.name, + WorkGroup: r.name, }) return err } -func (a *AthenaWorkGroup) Filter() error { +func (r *AthenaWorkGroup) Filter() error { // If this is the primary work group, // check if it's already had its configuration reset - if *a.name == "primary" { + if *r.name == "primary" { // Get workgroup configuration - wgConfigRes, err := a.svc.GetWorkGroup(&athena.GetWorkGroupInput{ - WorkGroup: a.name, + wgConfigRes, err := r.svc.GetWorkGroup(&athena.GetWorkGroupInput{ + WorkGroup: r.name, }) if err != nil { return err } // Get workgroup tags - wgTagsRes, err := a.svc.ListTagsForResource(&athena.ListTagsForResourceInput{ - ResourceARN: a.arn, + wgTagsRes, err := r.svc.ListTagsForResource(&athena.ListTagsForResourceInput{ + ResourceARN: r.arn, }) if err != nil { return err } - // If the workgroup is already in a "clean" state, then + // If the workgroup is already in r "clean" state, then // don't add it to our plan wgConfig := wgConfigRes.WorkGroup.Configuration isCleanConfig := wgConfig.BytesScannedCutoffPerQuery == nil && @@ -181,12 +181,12 @@ func (a *AthenaWorkGroup) Filter() error { return nil } -func (a *AthenaWorkGroup) Properties() types.Properties { +func (r *AthenaWorkGroup) Properties() types.Properties { return types.NewProperties(). - Set("Name", *a.name). - Set("ARN", *a.arn) + Set("Name", *r.name). + Set("ARN", *r.arn) } -func (a *AthenaWorkGroup) String() string { - return *a.name +func (r *AthenaWorkGroup) String() string { + return *r.name }