Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[New Resource] aws_glue_catalog_table_optimizer #38052

Merged
merged 27 commits into from
Aug 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a3087d3
skaff catalog_table_optimizer resource
t04glovern Jun 19, 2024
7f7852f
attempt to implement catalog_table_optimizer
t04glovern Jun 19, 2024
9485740
more fixes
t04glovern Jun 20, 2024
5bb8523
more fixes
t04glovern Jun 20, 2024
da6b86e
more fixes
t04glovern Jun 20, 2024
9079fc6
finished poc
t04glovern Jun 20, 2024
1981cad
fixes
t04glovern Jun 20, 2024
2374411
fixing acc test
t04glovern Jun 20, 2024
a246ba3
fixed unused imports
t04glovern Jun 20, 2024
01cedf2
Merge branch 'main' into f-glue-catalog-table-optimizer
johnsonaj Aug 21, 2024
e093685
aws_glue_catalog_table_optimizer: convert to plugin framework
johnsonaj Aug 22, 2024
0928852
aws_glue_catalog_table_optimizer: simplify tests
johnsonaj Aug 22, 2024
485c287
aws_glue_catalog_table_optimizer: simplify tests
johnsonaj Aug 22, 2024
877a411
Merge branch 'main' into f-glue-catalog-table-optimizer
johnsonaj Aug 22, 2024
de2fd1b
aws_glue_catalog_table_optimizer: set attributes for import
johnsonaj Aug 22, 2024
c61d4e3
chore: semgrep fix
johnsonaj Aug 22, 2024
650b70d
chore: refactor base test config
johnsonaj Aug 22, 2024
9efc36d
aws_glue_catalog_table_optimizer: add test for updating
johnsonaj Aug 22, 2024
00f2766
serialize tests
johnsonaj Aug 22, 2024
0afc62d
add CHANGELOG entry
johnsonaj Aug 22, 2024
cc4df03
aws_glue_catalog_table_optimizer: documentation
johnsonaj Aug 22, 2024
de40b07
chore: linters
johnsonaj Aug 22, 2024
88cd759
chore: markdown-lint
johnsonaj Aug 22, 2024
f237428
add missing documentation heading
johnsonaj Aug 22, 2024
9354df6
chore: providerdocs lint
johnsonaj Aug 22, 2024
6cb1dd4
chore: linter
johnsonaj Aug 22, 2024
3d5828d
chore: semgrep fix
johnsonaj Aug 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .changelog/38052.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_glue_catalog_table_optimizer
```
341 changes: 341 additions & 0 deletions internal/service/glue/catalog_table_optimizer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,341 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package glue

import (
"context"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/service/glue"
awstypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
"github.com/hashicorp/terraform-plugin-framework/path"
"github.com/hashicorp/terraform-plugin-framework/resource"
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
"github.com/hashicorp/terraform-plugin-framework/types"
"github.com/hashicorp/terraform-plugin-log/tflog"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-provider-aws/internal/create"
"github.com/hashicorp/terraform-provider-aws/internal/errs"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
"github.com/hashicorp/terraform-provider-aws/internal/framework"
fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
"github.com/hashicorp/terraform-provider-aws/names"
)

// @FrameworkResource("aws_glue_catalog_table_optimizer",name="Catalog Table Optimizer")
func newResourceCatalogTableOptimizer(context.Context) (resource.ResourceWithConfigure, error) {
r := &resourceCatalogTableOptimizer{}

return r, nil
}

const (
ResNameCatalogTableOptimizer = "Catalog Table Optimizer"

idParts = 4
)

type resourceCatalogTableOptimizer struct {
framework.ResourceWithConfigure
}

func (r *resourceCatalogTableOptimizer) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) {
response.TypeName = "aws_glue_catalog_table_optimizer"
}

func (r *resourceCatalogTableOptimizer) Schema(ctx context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) {
s := schema.Schema{
Attributes: map[string]schema.Attribute{
names.AttrCatalogID: schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
names.AttrDatabaseName: schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
names.AttrTableName: schema.StringAttribute{
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
names.AttrType: schema.StringAttribute{
CustomType: fwtypes.StringEnumType[awstypes.TableOptimizerType](),
Required: true,
PlanModifiers: []planmodifier.String{
stringplanmodifier.RequiresReplace(),
},
},
},
Blocks: map[string]schema.Block{
names.AttrConfiguration: schema.ListNestedBlock{
CustomType: fwtypes.NewListNestedObjectTypeOf[configurationData](ctx),
Validators: []validator.List{
listvalidator.IsRequired(),
listvalidator.SizeAtMost(1),
},
NestedObject: schema.NestedBlockObject{
Attributes: map[string]schema.Attribute{
names.AttrEnabled: schema.BoolAttribute{
Required: true,
},
names.AttrRoleARN: schema.StringAttribute{
CustomType: fwtypes.ARNType,
Required: true,
},
},
},
},
},
}

response.Schema = s
}

func (r *resourceCatalogTableOptimizer) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
conn := r.Meta().GlueClient(ctx)
var plan resourceCatalogTableOptimizerData

response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)

if response.Diagnostics.HasError() {
return
}

input := glue.CreateTableOptimizerInput{}
response.Diagnostics.Append(fwflex.Expand(ctx, plan, &input, fwflex.WithFieldNamePrefix("TableOptimizer"))...)

if response.Diagnostics.HasError() {
return
}

err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError {
_, err := conn.CreateTableOptimizer(ctx, &input)
if err != nil {
// Retry IAM propagation errors
if errs.IsAErrorMessageContains[*awstypes.AccessDeniedException](err, "does not have the correct trust policies and is unable to be assumed by our service") {
return retry.RetryableError(err)
}
if errs.IsAErrorMessageContains[*awstypes.AccessDeniedException](err, "does not have the proper IAM permissions to call Glue APIs") {
return retry.RetryableError(err)
}
if errs.IsAErrorMessageContains[*awstypes.AccessDeniedException](err, "is not authorized to perform") {
return retry.RetryableError(err)
}

return retry.NonRetryableError(err)
}
return nil
})

if tfresource.TimedOut(err) {
_, err = conn.CreateTableOptimizer(ctx, &input)
}

if err != nil {
id, _ := flex.FlattenResourceId([]string{
plan.CatalogID.ValueString(),
plan.DatabaseName.ValueString(),
plan.TableName.ValueString(),
plan.Type.ValueString(),
}, idParts, false)

response.Diagnostics.AddError(
create.ProblemStandardMessage(names.Glue, create.ErrActionCreating, ResNameCatalogTableOptimizer, id, err),
err.Error(),
)
return
}

response.Diagnostics.Append(response.State.Set(ctx, &plan)...)
}

func (r *resourceCatalogTableOptimizer) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
conn := r.Meta().GlueClient(ctx)
var data resourceCatalogTableOptimizerData

response.Diagnostics.Append(request.State.Get(ctx, &data)...)

if response.Diagnostics.HasError() {
return
}

output, err := findCatalogTableOptimizer(ctx, conn, data.CatalogID.ValueString(), data.DatabaseName.ValueString(), data.TableName.ValueString(), data.Type.ValueString())

if err != nil {
id, _ := flex.FlattenResourceId([]string{
data.CatalogID.ValueString(),
data.DatabaseName.ValueString(),
data.TableName.ValueString(),
data.Type.ValueString(),
}, idParts, false)

response.Diagnostics.AddError(
create.ProblemStandardMessage(names.Glue, create.ErrActionReading, ResNameCatalogTableOptimizer, id, err),
err.Error(),
)
return
}

response.Diagnostics.Append(fwflex.Flatten(ctx, output.TableOptimizer, &data)...)

if response.Diagnostics.HasError() {
return
}

response.Diagnostics.Append(response.State.Set(ctx, &data)...)
}

func (r *resourceCatalogTableOptimizer) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
conn := r.Meta().GlueClient(ctx)

var plan, state resourceCatalogTableOptimizerData
response.Diagnostics.Append(request.State.Get(ctx, &state)...)
response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)

if response.Diagnostics.HasError() {
return
}

if !plan.Configuration.Equal(state.Configuration) {
input := glue.UpdateTableOptimizerInput{}
response.Diagnostics.Append(fwflex.Expand(ctx, plan, &input, fwflex.WithFieldNamePrefix("TableOptimizer"))...)

if response.Diagnostics.HasError() {
return
}

_, err := conn.UpdateTableOptimizer(ctx, &input)

if err != nil {
id, _ := flex.FlattenResourceId([]string{
state.CatalogID.ValueString(),
state.DatabaseName.ValueString(),
state.TableName.ValueString(),
state.Type.ValueString(),
}, idParts, false)

response.Diagnostics.AddError(
create.ProblemStandardMessage(names.Glue, create.ErrActionUpdating, ResNameCatalogTableOptimizer, id, err),
err.Error(),
)
return
}
}

response.Diagnostics.Append(response.State.Set(ctx, &plan)...)
}

func (r *resourceCatalogTableOptimizer) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
conn := r.Meta().GlueClient(ctx)
var data resourceCatalogTableOptimizerData

response.Diagnostics.Append(request.State.Get(ctx, &data)...)

if response.Diagnostics.HasError() {
return
}

tflog.Debug(ctx, "deleting Glue Catalog Table Optimizer", map[string]interface{}{
names.AttrCatalogID: data.CatalogID.ValueString(),
names.AttrDatabaseName: data.DatabaseName.ValueString(),
names.AttrTableName: data.TableName.ValueString(),
names.AttrType: data.Type.ValueString(),
})

_, err := conn.DeleteTableOptimizer(ctx, &glue.DeleteTableOptimizerInput{
CatalogId: data.CatalogID.ValueStringPointer(),
DatabaseName: data.DatabaseName.ValueStringPointer(),
TableName: data.TableName.ValueStringPointer(),
Type: data.Type.ValueEnum(),
})

if errs.IsA[*awstypes.EntityNotFoundException](err) {
return
}

if err != nil {
id, _ := flex.FlattenResourceId([]string{
data.CatalogID.ValueString(),
data.DatabaseName.ValueString(),
data.TableName.ValueString(),
data.Type.ValueString(),
}, idParts, false)

response.Diagnostics.AddError(
create.ProblemStandardMessage(names.Glue, create.ErrActionDeleting, ResNameCatalogTableOptimizer, id, err),
err.Error(),
)
return
}
}

func (r *resourceCatalogTableOptimizer) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
parts, err := flex.ExpandResourceId(request.ID, idParts, false)

if err != nil {
response.Diagnostics.AddError(
create.ProblemStandardMessage(names.Glue, create.ErrActionImporting, ResNameCatalogTableOptimizer, request.ID, err),
err.Error(),
)
return
}

response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrCatalogID), parts[0])...)
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrDatabaseName), parts[1])...)
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrTableName), parts[2])...)
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrType), parts[3])...)
}

type resourceCatalogTableOptimizerData struct {
CatalogID types.String `tfsdk:"catalog_id"`
Configuration fwtypes.ListNestedObjectValueOf[configurationData] `tfsdk:"configuration"`
DatabaseName types.String `tfsdk:"database_name"`
TableName types.String `tfsdk:"table_name"`
Type fwtypes.StringEnum[awstypes.TableOptimizerType] `tfsdk:"type"`
}

type configurationData struct {
Enabled types.Bool `tfsdk:"enabled"`
RoleARN fwtypes.ARN `tfsdk:"role_arn"`
}

func findCatalogTableOptimizer(ctx context.Context, conn *glue.Client, catalogID, dbName, tableName, optimizerType string) (*glue.GetTableOptimizerOutput, error) {
input := &glue.GetTableOptimizerInput{
CatalogId: aws.String(catalogID),
DatabaseName: aws.String(dbName),
TableName: aws.String(tableName),
Type: awstypes.TableOptimizerType(optimizerType),
}

output, err := conn.GetTableOptimizer(ctx, input)

if errs.IsA[*awstypes.EntityNotFoundException](err) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return output, nil
}
Loading
Loading