Skip to content

Commit 8490fa5

Browse files
authored
Merge pull request #38052 from t04glovern/f-glue-catalog-table-optimizer
[New Resource] aws_glue_catalog_table_optimizer
2 parents 1b0d134 + 3d5828d commit 8490fa5

7 files changed

+749
-1
lines changed

.changelog/38052.txt

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
```release-note:new-resource
2+
aws_glue_catalog_table_optimizer
3+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,341 @@
1+
// Copyright (c) HashiCorp, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package glue
5+
6+
import (
7+
"context"
8+
9+
"github.com/aws/aws-sdk-go-v2/aws"
10+
"github.com/aws/aws-sdk-go-v2/service/glue"
11+
awstypes "github.com/aws/aws-sdk-go-v2/service/glue/types"
12+
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
13+
"github.com/hashicorp/terraform-plugin-framework/path"
14+
"github.com/hashicorp/terraform-plugin-framework/resource"
15+
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
16+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
17+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
18+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
19+
"github.com/hashicorp/terraform-plugin-framework/types"
20+
"github.com/hashicorp/terraform-plugin-log/tflog"
21+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
22+
"github.com/hashicorp/terraform-provider-aws/internal/create"
23+
"github.com/hashicorp/terraform-provider-aws/internal/errs"
24+
"github.com/hashicorp/terraform-provider-aws/internal/flex"
25+
"github.com/hashicorp/terraform-provider-aws/internal/framework"
26+
fwflex "github.com/hashicorp/terraform-provider-aws/internal/framework/flex"
27+
fwtypes "github.com/hashicorp/terraform-provider-aws/internal/framework/types"
28+
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
29+
"github.com/hashicorp/terraform-provider-aws/names"
30+
)
31+
32+
// @FrameworkResource("aws_glue_catalog_table_optimizer",name="Catalog Table Optimizer")
33+
func newResourceCatalogTableOptimizer(context.Context) (resource.ResourceWithConfigure, error) {
34+
r := &resourceCatalogTableOptimizer{}
35+
36+
return r, nil
37+
}
38+
39+
const (
40+
ResNameCatalogTableOptimizer = "Catalog Table Optimizer"
41+
42+
idParts = 4
43+
)
44+
45+
type resourceCatalogTableOptimizer struct {
46+
framework.ResourceWithConfigure
47+
}
48+
49+
func (r *resourceCatalogTableOptimizer) Metadata(_ context.Context, _ resource.MetadataRequest, response *resource.MetadataResponse) {
50+
response.TypeName = "aws_glue_catalog_table_optimizer"
51+
}
52+
53+
func (r *resourceCatalogTableOptimizer) Schema(ctx context.Context, _ resource.SchemaRequest, response *resource.SchemaResponse) {
54+
s := schema.Schema{
55+
Attributes: map[string]schema.Attribute{
56+
names.AttrCatalogID: schema.StringAttribute{
57+
Required: true,
58+
PlanModifiers: []planmodifier.String{
59+
stringplanmodifier.RequiresReplace(),
60+
},
61+
},
62+
names.AttrDatabaseName: schema.StringAttribute{
63+
Required: true,
64+
PlanModifiers: []planmodifier.String{
65+
stringplanmodifier.RequiresReplace(),
66+
},
67+
},
68+
names.AttrTableName: schema.StringAttribute{
69+
Required: true,
70+
PlanModifiers: []planmodifier.String{
71+
stringplanmodifier.RequiresReplace(),
72+
},
73+
},
74+
names.AttrType: schema.StringAttribute{
75+
CustomType: fwtypes.StringEnumType[awstypes.TableOptimizerType](),
76+
Required: true,
77+
PlanModifiers: []planmodifier.String{
78+
stringplanmodifier.RequiresReplace(),
79+
},
80+
},
81+
},
82+
Blocks: map[string]schema.Block{
83+
names.AttrConfiguration: schema.ListNestedBlock{
84+
CustomType: fwtypes.NewListNestedObjectTypeOf[configurationData](ctx),
85+
Validators: []validator.List{
86+
listvalidator.IsRequired(),
87+
listvalidator.SizeAtMost(1),
88+
},
89+
NestedObject: schema.NestedBlockObject{
90+
Attributes: map[string]schema.Attribute{
91+
names.AttrEnabled: schema.BoolAttribute{
92+
Required: true,
93+
},
94+
names.AttrRoleARN: schema.StringAttribute{
95+
CustomType: fwtypes.ARNType,
96+
Required: true,
97+
},
98+
},
99+
},
100+
},
101+
},
102+
}
103+
104+
response.Schema = s
105+
}
106+
107+
func (r *resourceCatalogTableOptimizer) Create(ctx context.Context, request resource.CreateRequest, response *resource.CreateResponse) {
108+
conn := r.Meta().GlueClient(ctx)
109+
var plan resourceCatalogTableOptimizerData
110+
111+
response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)
112+
113+
if response.Diagnostics.HasError() {
114+
return
115+
}
116+
117+
input := glue.CreateTableOptimizerInput{}
118+
response.Diagnostics.Append(fwflex.Expand(ctx, plan, &input, fwflex.WithFieldNamePrefix("TableOptimizer"))...)
119+
120+
if response.Diagnostics.HasError() {
121+
return
122+
}
123+
124+
err := retry.RetryContext(ctx, propagationTimeout, func() *retry.RetryError {
125+
_, err := conn.CreateTableOptimizer(ctx, &input)
126+
if err != nil {
127+
// Retry IAM propagation errors
128+
if errs.IsAErrorMessageContains[*awstypes.AccessDeniedException](err, "does not have the correct trust policies and is unable to be assumed by our service") {
129+
return retry.RetryableError(err)
130+
}
131+
if errs.IsAErrorMessageContains[*awstypes.AccessDeniedException](err, "does not have the proper IAM permissions to call Glue APIs") {
132+
return retry.RetryableError(err)
133+
}
134+
if errs.IsAErrorMessageContains[*awstypes.AccessDeniedException](err, "is not authorized to perform") {
135+
return retry.RetryableError(err)
136+
}
137+
138+
return retry.NonRetryableError(err)
139+
}
140+
return nil
141+
})
142+
143+
if tfresource.TimedOut(err) {
144+
_, err = conn.CreateTableOptimizer(ctx, &input)
145+
}
146+
147+
if err != nil {
148+
id, _ := flex.FlattenResourceId([]string{
149+
plan.CatalogID.ValueString(),
150+
plan.DatabaseName.ValueString(),
151+
plan.TableName.ValueString(),
152+
plan.Type.ValueString(),
153+
}, idParts, false)
154+
155+
response.Diagnostics.AddError(
156+
create.ProblemStandardMessage(names.Glue, create.ErrActionCreating, ResNameCatalogTableOptimizer, id, err),
157+
err.Error(),
158+
)
159+
return
160+
}
161+
162+
response.Diagnostics.Append(response.State.Set(ctx, &plan)...)
163+
}
164+
165+
func (r *resourceCatalogTableOptimizer) Read(ctx context.Context, request resource.ReadRequest, response *resource.ReadResponse) {
166+
conn := r.Meta().GlueClient(ctx)
167+
var data resourceCatalogTableOptimizerData
168+
169+
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
170+
171+
if response.Diagnostics.HasError() {
172+
return
173+
}
174+
175+
output, err := findCatalogTableOptimizer(ctx, conn, data.CatalogID.ValueString(), data.DatabaseName.ValueString(), data.TableName.ValueString(), data.Type.ValueString())
176+
177+
if err != nil {
178+
id, _ := flex.FlattenResourceId([]string{
179+
data.CatalogID.ValueString(),
180+
data.DatabaseName.ValueString(),
181+
data.TableName.ValueString(),
182+
data.Type.ValueString(),
183+
}, idParts, false)
184+
185+
response.Diagnostics.AddError(
186+
create.ProblemStandardMessage(names.Glue, create.ErrActionReading, ResNameCatalogTableOptimizer, id, err),
187+
err.Error(),
188+
)
189+
return
190+
}
191+
192+
response.Diagnostics.Append(fwflex.Flatten(ctx, output.TableOptimizer, &data)...)
193+
194+
if response.Diagnostics.HasError() {
195+
return
196+
}
197+
198+
response.Diagnostics.Append(response.State.Set(ctx, &data)...)
199+
}
200+
201+
func (r *resourceCatalogTableOptimizer) Update(ctx context.Context, request resource.UpdateRequest, response *resource.UpdateResponse) {
202+
conn := r.Meta().GlueClient(ctx)
203+
204+
var plan, state resourceCatalogTableOptimizerData
205+
response.Diagnostics.Append(request.State.Get(ctx, &state)...)
206+
response.Diagnostics.Append(request.Plan.Get(ctx, &plan)...)
207+
208+
if response.Diagnostics.HasError() {
209+
return
210+
}
211+
212+
if !plan.Configuration.Equal(state.Configuration) {
213+
input := glue.UpdateTableOptimizerInput{}
214+
response.Diagnostics.Append(fwflex.Expand(ctx, plan, &input, fwflex.WithFieldNamePrefix("TableOptimizer"))...)
215+
216+
if response.Diagnostics.HasError() {
217+
return
218+
}
219+
220+
_, err := conn.UpdateTableOptimizer(ctx, &input)
221+
222+
if err != nil {
223+
id, _ := flex.FlattenResourceId([]string{
224+
state.CatalogID.ValueString(),
225+
state.DatabaseName.ValueString(),
226+
state.TableName.ValueString(),
227+
state.Type.ValueString(),
228+
}, idParts, false)
229+
230+
response.Diagnostics.AddError(
231+
create.ProblemStandardMessage(names.Glue, create.ErrActionUpdating, ResNameCatalogTableOptimizer, id, err),
232+
err.Error(),
233+
)
234+
return
235+
}
236+
}
237+
238+
response.Diagnostics.Append(response.State.Set(ctx, &plan)...)
239+
}
240+
241+
func (r *resourceCatalogTableOptimizer) Delete(ctx context.Context, request resource.DeleteRequest, response *resource.DeleteResponse) {
242+
conn := r.Meta().GlueClient(ctx)
243+
var data resourceCatalogTableOptimizerData
244+
245+
response.Diagnostics.Append(request.State.Get(ctx, &data)...)
246+
247+
if response.Diagnostics.HasError() {
248+
return
249+
}
250+
251+
tflog.Debug(ctx, "deleting Glue Catalog Table Optimizer", map[string]interface{}{
252+
names.AttrCatalogID: data.CatalogID.ValueString(),
253+
names.AttrDatabaseName: data.DatabaseName.ValueString(),
254+
names.AttrTableName: data.TableName.ValueString(),
255+
names.AttrType: data.Type.ValueString(),
256+
})
257+
258+
_, err := conn.DeleteTableOptimizer(ctx, &glue.DeleteTableOptimizerInput{
259+
CatalogId: data.CatalogID.ValueStringPointer(),
260+
DatabaseName: data.DatabaseName.ValueStringPointer(),
261+
TableName: data.TableName.ValueStringPointer(),
262+
Type: data.Type.ValueEnum(),
263+
})
264+
265+
if errs.IsA[*awstypes.EntityNotFoundException](err) {
266+
return
267+
}
268+
269+
if err != nil {
270+
id, _ := flex.FlattenResourceId([]string{
271+
data.CatalogID.ValueString(),
272+
data.DatabaseName.ValueString(),
273+
data.TableName.ValueString(),
274+
data.Type.ValueString(),
275+
}, idParts, false)
276+
277+
response.Diagnostics.AddError(
278+
create.ProblemStandardMessage(names.Glue, create.ErrActionDeleting, ResNameCatalogTableOptimizer, id, err),
279+
err.Error(),
280+
)
281+
return
282+
}
283+
}
284+
285+
func (r *resourceCatalogTableOptimizer) ImportState(ctx context.Context, request resource.ImportStateRequest, response *resource.ImportStateResponse) {
286+
parts, err := flex.ExpandResourceId(request.ID, idParts, false)
287+
288+
if err != nil {
289+
response.Diagnostics.AddError(
290+
create.ProblemStandardMessage(names.Glue, create.ErrActionImporting, ResNameCatalogTableOptimizer, request.ID, err),
291+
err.Error(),
292+
)
293+
return
294+
}
295+
296+
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrCatalogID), parts[0])...)
297+
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrDatabaseName), parts[1])...)
298+
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrTableName), parts[2])...)
299+
response.Diagnostics.Append(response.State.SetAttribute(ctx, path.Root(names.AttrType), parts[3])...)
300+
}
301+
302+
type resourceCatalogTableOptimizerData struct {
303+
CatalogID types.String `tfsdk:"catalog_id"`
304+
Configuration fwtypes.ListNestedObjectValueOf[configurationData] `tfsdk:"configuration"`
305+
DatabaseName types.String `tfsdk:"database_name"`
306+
TableName types.String `tfsdk:"table_name"`
307+
Type fwtypes.StringEnum[awstypes.TableOptimizerType] `tfsdk:"type"`
308+
}
309+
310+
type configurationData struct {
311+
Enabled types.Bool `tfsdk:"enabled"`
312+
RoleARN fwtypes.ARN `tfsdk:"role_arn"`
313+
}
314+
315+
func findCatalogTableOptimizer(ctx context.Context, conn *glue.Client, catalogID, dbName, tableName, optimizerType string) (*glue.GetTableOptimizerOutput, error) {
316+
input := &glue.GetTableOptimizerInput{
317+
CatalogId: aws.String(catalogID),
318+
DatabaseName: aws.String(dbName),
319+
TableName: aws.String(tableName),
320+
Type: awstypes.TableOptimizerType(optimizerType),
321+
}
322+
323+
output, err := conn.GetTableOptimizer(ctx, input)
324+
325+
if errs.IsA[*awstypes.EntityNotFoundException](err) {
326+
return nil, &retry.NotFoundError{
327+
LastError: err,
328+
LastRequest: input,
329+
}
330+
}
331+
332+
if err != nil {
333+
return nil, err
334+
}
335+
336+
if output == nil {
337+
return nil, tfresource.NewEmptyResultError(input)
338+
}
339+
340+
return output, nil
341+
}

0 commit comments

Comments
 (0)