Skip to content

Commit cec3221

Browse files
authored
Merge pull request #32201 from hashicorp/b-lakeformation-tags
aws_lakeformation_resource_lf_tags: Correctly reads table column tags with inheritance
2 parents a745b5c + c313b2b commit cec3221

File tree

4 files changed

+371
-108
lines changed

4 files changed

+371
-108
lines changed

internal/create/errors.go

+23-4
Original file line numberDiff line numberDiff line change
@@ -49,14 +49,24 @@ func Error(service, action, resource, id string, gotError error) error {
4949
return errors.New(ProblemStandardMessage(service, action, resource, id, gotError))
5050
}
5151

52+
// AddError returns diag.Diagnostics with an additional diag.Diagnostic containing
53+
// an error using a standardized problem message
54+
func AddError(diags diag.Diagnostics, service, action, resource, id string, gotError error) diag.Diagnostics {
55+
return append(diags, newError(service, action, resource, id, gotError))
56+
}
57+
5258
// DiagError returns a 1-length diag.Diagnostics with a diag.Error-level diag.Diagnostic
5359
// with a standardized error message
5460
func DiagError(service, action, resource, id string, gotError error) diag.Diagnostics {
5561
return diag.Diagnostics{
56-
diag.Diagnostic{
57-
Severity: diag.Error,
58-
Summary: ProblemStandardMessage(service, action, resource, id, gotError),
59-
},
62+
newError(service, action, resource, id, gotError),
63+
}
64+
}
65+
66+
func newError(service, action, resource, id string, gotError error) diag.Diagnostic {
67+
return diag.Diagnostic{
68+
Severity: diag.Error,
69+
Summary: ProblemStandardMessage(service, action, resource, id, gotError),
6070
}
6171
}
6272

@@ -99,6 +109,15 @@ func AddWarning(diags diag.Diagnostics, service, action, resource, id string, go
99109
)
100110
}
101111

112+
func AddWarningMessage(diags diag.Diagnostics, service, action, resource, id, message string) diag.Diagnostics {
113+
return append(diags,
114+
diag.Diagnostic{
115+
Severity: diag.Warning,
116+
Summary: ProblemStandardMessage(service, action, resource, id, fmt.Errorf(message)),
117+
},
118+
)
119+
}
120+
102121
// AddWarningNotFoundRemoveState returns diag.Diagnostics with an additional diag.Diagnostic containing
103122
// a warning using a standardized problem message
104123
func AddWarningNotFoundRemoveState(service, action, resource, id string) diag.Diagnostics {

internal/service/lakeformation/lakeformation_test.go

+7-5
Original file line numberDiff line numberDiff line change
@@ -62,11 +62,13 @@ func TestAccLakeFormation_serial(t *testing.T) {
6262
"valuesOverFifty": testAccLFTag_Values_overFifty,
6363
},
6464
"ResourceLFTags": {
65-
"basic": testAccResourceLFTags_basic,
66-
"database": testAccResourceLFTags_database,
67-
"databaseMultiple": testAccResourceLFTags_databaseMultiple,
68-
"table": testAccResourceLFTags_table,
69-
"tableWithColumns": testAccResourceLFTags_tableWithColumns,
65+
"basic": testAccResourceLFTags_basic,
66+
"database": testAccResourceLFTags_database,
67+
"databaseMultipleTags": testAccResourceLFTags_databaseMultipleTags,
68+
"disappears": testAccResourceLFTags_disappears,
69+
"hierarchy": testAccResourceLFTags_hierarchy,
70+
"table": testAccResourceLFTags_table,
71+
"tableWithColumns": testAccResourceLFTags_tableWithColumns,
7072
},
7173
}
7274

internal/service/lakeformation/resource_lf_tags.go

+112-80
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import (
44
"bytes"
55
"context"
66
"fmt"
7-
"log"
87
"reflect"
98
"time"
109

@@ -18,6 +17,7 @@ import (
1817
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
1918
"github.com/hashicorp/terraform-provider-aws/internal/conns"
2019
"github.com/hashicorp/terraform-provider-aws/internal/create"
20+
"github.com/hashicorp/terraform-provider-aws/internal/errs"
2121
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
2222
"github.com/hashicorp/terraform-provider-aws/internal/verify"
2323
"github.com/hashicorp/terraform-provider-aws/names"
@@ -223,31 +223,27 @@ func ResourceResourceLFTags() *schema.Resource {
223223
}
224224

225225
func resourceResourceLFTagsCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
226+
var diags diag.Diagnostics
227+
226228
conn := meta.(*conns.AWSClient).LakeFormationConn(ctx)
227229

228-
input := &lakeformation.AddLFTagsToResourceInput{
229-
Resource: &lakeformation.Resource{},
230-
}
230+
input := &lakeformation.AddLFTagsToResourceInput{}
231231

232232
if v, ok := d.GetOk("catalog_id"); ok {
233233
input.CatalogId = aws.String(v.(string))
234234
}
235235

236-
if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
237-
input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{}))
238-
}
239-
240236
if v, ok := d.GetOk("lf_tag"); ok && v.(*schema.Set).Len() > 0 {
241237
input.LFTags = expandLFTagPairs(v.(*schema.Set).List())
242238
}
243239

244-
if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
245-
input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{}))
240+
tagger, ds := lfTagsTagger(d)
241+
diags = append(diags, ds...)
242+
if diags.HasError() {
243+
return diags
246244
}
247245

248-
if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
249-
input.Resource.TableWithColumns = expandTableColumnsResource(v.([]interface{})[0].(map[string]interface{}))
250-
}
246+
input.Resource = tagger.ExpandResource(d)
251247

252248
var output *lakeformation.AddLFTagsToResourceOutput
253249
err := retry.RetryContext(ctx, IAMPropagationTimeout, func() *retry.RetryError {
@@ -271,130 +267,93 @@ func resourceResourceLFTagsCreate(ctx context.Context, d *schema.ResourceData, m
271267
}
272268

273269
if err != nil {
274-
return create.DiagError(names.LakeFormation, create.ErrActionCreating, ResNameLFTags, input.String(), err)
270+
return create.AddError(diags, names.LakeFormation, create.ErrActionCreating, ResNameLFTags, input.String(), err)
275271
}
276272

277-
diags := diag.Diagnostics{}
278-
279273
if output != nil && len(output.Failures) > 0 {
280274
for _, v := range output.Failures {
281275
if v.LFTag == nil || v.Error == nil {
282276
continue
283277
}
284278

285-
diags = create.AddWarning(
286-
diags,
279+
diags = create.AddError(diags,
287280
names.LakeFormation,
288281
create.ErrActionCreating,
289282
ResNameLFTags,
290283
fmt.Sprintf("catalog id:%s, tag key:%s, values:%+v", aws.StringValue(v.LFTag.CatalogId), aws.StringValue(v.LFTag.TagKey), aws.StringValueSlice(v.LFTag.TagValues)),
291284
awserr.New(aws.StringValue(v.Error.ErrorCode), aws.StringValue(v.Error.ErrorMessage), nil),
292285
)
293286
}
294-
295-
if len(diags) == len(input.LFTags) {
296-
return append(diags,
297-
diag.Diagnostic{
298-
Severity: diag.Error,
299-
Summary: create.ProblemStandardMessage(names.LakeFormation, create.ErrActionCreating, ResNameLFTags, "", fmt.Errorf("attempted to add %d tags, %d failures", len(input.LFTags), len(diags))),
300-
},
301-
)
302-
}
287+
}
288+
if diags.HasError() {
289+
return diags
303290
}
304291

305292
d.SetId(fmt.Sprintf("%d", create.StringHashcode(input.String())))
306293

307-
return append(resourceResourceLFTagsRead(ctx, d, meta), diags...)
294+
return append(diags, resourceResourceLFTagsRead(ctx, d, meta)...)
308295
}
309296

310297
func resourceResourceLFTagsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
298+
var diags diag.Diagnostics
299+
311300
conn := meta.(*conns.AWSClient).LakeFormationConn(ctx)
312301

313302
input := &lakeformation.GetResourceLFTagsInput{
314-
Resource: &lakeformation.Resource{},
315303
ShowAssignedLFTags: aws.Bool(true),
316304
}
317305

318306
if v, ok := d.GetOk("catalog_id"); ok {
319307
input.CatalogId = aws.String(v.(string))
320308
}
321309

322-
if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
323-
input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{}))
324-
}
325-
326-
if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
327-
input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{}))
310+
tagger, ds := lfTagsTagger(d)
311+
diags = append(diags, ds...)
312+
if diags.HasError() {
313+
return diags
328314
}
329315

330-
if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
331-
input.Resource.TableWithColumns = expandTableColumnsResource(v.([]interface{})[0].(map[string]interface{}))
332-
}
316+
input.Resource = tagger.ExpandResource(d)
333317

334318
output, err := conn.GetResourceLFTagsWithContext(ctx, input)
335319

336320
if err != nil {
337-
return create.DiagError(names.LakeFormation, create.ErrActionReading, ResNameLFTags, d.Id(), err)
338-
}
339-
340-
if len(output.LFTagOnDatabase) > 0 {
341-
if err := d.Set("lf_tag", flattenLFTagPairs(output.LFTagOnDatabase)); err != nil {
342-
return create.DiagError(names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err)
343-
}
344-
}
345-
346-
if len(output.LFTagsOnColumns) > 0 {
347-
for _, v := range output.LFTagsOnColumns {
348-
if aws.StringValue(v.Name) != d.Get("table_with_columns.0.name").(string) {
349-
continue
350-
}
351-
352-
if err := d.Set("lf_tag", flattenLFTagPairs(v.LFTags)); err != nil {
353-
return create.DiagError(names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err)
354-
}
355-
}
321+
return create.AddError(diags, names.LakeFormation, create.ErrActionReading, ResNameLFTags, d.Id(), err)
356322
}
357323

358-
if len(output.LFTagsOnTable) > 0 {
359-
if err := d.Set("lf_tag", flattenLFTagPairs(output.LFTagsOnTable)); err != nil {
360-
return create.DiagError(names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err)
361-
}
324+
if err := d.Set("lf_tag", tagger.FlattenTags(output)); err != nil {
325+
return create.AddError(diags, names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err)
362326
}
363327

364-
return nil
328+
return diags
365329
}
366330

367331
func resourceResourceLFTagsDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
332+
var diags diag.Diagnostics
333+
368334
conn := meta.(*conns.AWSClient).LakeFormationConn(ctx)
369335

370-
input := &lakeformation.RemoveLFTagsFromResourceInput{
371-
Resource: &lakeformation.Resource{},
372-
}
336+
input := &lakeformation.RemoveLFTagsFromResourceInput{}
373337

374338
if v, ok := d.GetOk("catalog_id"); ok {
375339
input.CatalogId = aws.String(v.(string))
376340
}
377341

378-
if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
379-
input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{}))
380-
}
381-
382342
if v, ok := d.GetOk("lf_tag"); ok && v.(*schema.Set).Len() > 0 {
383343
input.LFTags = expandLFTagPairs(v.(*schema.Set).List())
384344
}
385345

386-
if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
387-
input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{}))
346+
tagger, ds := lfTagsTagger(d)
347+
diags = append(diags, ds...)
348+
if diags.HasError() {
349+
return diags
388350
}
389351

390-
if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
391-
input.Resource.TableWithColumns = expandTableColumnsResource(v.([]interface{})[0].(map[string]interface{}))
392-
}
352+
input.Resource = tagger.ExpandResource(d)
393353

394-
if input.Resource == nil || reflect.DeepEqual(input.Resource, &lakeformation.Resource{}) {
354+
if input.Resource == nil || reflect.DeepEqual(input.Resource, &lakeformation.Resource{}) || len(input.LFTags) == 0 {
395355
// if resource is empty, don't delete = it won't delete anything since this is the predicate
396-
log.Printf("[WARN] No Lake Formation Resource LF Tags to remove")
397-
return nil
356+
return create.AddWarningMessage(diags, names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), "no LF-Tags to remove")
398357
}
399358

400359
err := retry.RetryContext(ctx, d.Timeout(schema.TimeoutDelete), func() *retry.RetryError {
@@ -408,7 +367,7 @@ func resourceResourceLFTagsDelete(ctx context.Context, d *schema.ResourceData, m
408367
return retry.RetryableError(err)
409368
}
410369

411-
return retry.NonRetryableError(fmt.Errorf("unable to revoke Lake Formation Permissions: %w", err))
370+
return retry.NonRetryableError(fmt.Errorf("removing Lake Formation LF-Tags: %w", err))
412371
}
413372
return nil
414373
})
@@ -418,10 +377,83 @@ func resourceResourceLFTagsDelete(ctx context.Context, d *schema.ResourceData, m
418377
}
419378

420379
if err != nil {
421-
return create.DiagError(names.LakeFormation, create.ErrActionDeleting, ResNameLFTags, d.Id(), err)
380+
return create.AddError(diags, names.LakeFormation, create.ErrActionDeleting, ResNameLFTags, d.Id(), err)
381+
}
382+
383+
return diags
384+
}
385+
386+
func lfTagsTagger(d *schema.ResourceData) (tagger, diag.Diagnostics) {
387+
var diags diag.Diagnostics
388+
if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
389+
return &databaseTagger{}, diags
390+
} else if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
391+
return &tableTagger{}, diags
392+
} else if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil {
393+
return &columnTagger{}, diags
394+
} else {
395+
diags = append(diags, errs.NewErrorDiagnostic(
396+
"Invalid Lake Formation Resource Type",
397+
"An unexpected error occurred while resolving the Lake Formation Resource type. "+
398+
"This is always an error in the provider. "+
399+
"Please report the following to the provider developer:\n\n"+
400+
"No Lake Formation Resource defined.",
401+
))
402+
return nil, diags
403+
}
404+
}
405+
406+
type tagger interface {
407+
ExpandResource(*schema.ResourceData) *lakeformation.Resource
408+
FlattenTags(*lakeformation.GetResourceLFTagsOutput) []any
409+
}
410+
411+
type databaseTagger struct{}
412+
413+
func (t *databaseTagger) ExpandResource(d *schema.ResourceData) *lakeformation.Resource {
414+
v := d.Get("database").([]any)[0].(map[string]any)
415+
return &lakeformation.Resource{
416+
Database: ExpandDatabaseResource(v),
417+
}
418+
}
419+
420+
func (t *databaseTagger) FlattenTags(output *lakeformation.GetResourceLFTagsOutput) []any {
421+
return flattenLFTagPairs(output.LFTagOnDatabase)
422+
}
423+
424+
type tableTagger struct{}
425+
426+
func (t *tableTagger) ExpandResource(d *schema.ResourceData) *lakeformation.Resource {
427+
v := d.Get("table").([]any)[0].(map[string]any)
428+
return &lakeformation.Resource{
429+
Table: ExpandTableResource(v),
430+
}
431+
}
432+
433+
func (t *tableTagger) FlattenTags(output *lakeformation.GetResourceLFTagsOutput) []any {
434+
return flattenLFTagPairs(output.LFTagsOnTable)
435+
}
436+
437+
type columnTagger struct{}
438+
439+
func (t *columnTagger) ExpandResource(d *schema.ResourceData) *lakeformation.Resource {
440+
v := d.Get("table_with_columns").([]any)[0].(map[string]any)
441+
return &lakeformation.Resource{
442+
TableWithColumns: expandTableColumnsResource(v),
443+
}
444+
}
445+
446+
func (t *columnTagger) FlattenTags(output *lakeformation.GetResourceLFTagsOutput) []any {
447+
if len(output.LFTagsOnColumns) == 0 {
448+
return []any{}
449+
}
450+
451+
tags := output.LFTagsOnColumns[0]
452+
if tags == nil {
453+
return []any{}
422454
}
423455

424-
return nil
456+
return flattenLFTagPairs(tags.LFTags)
425457
}
426458

427459
func lfTagsHash(v interface{}) int {

0 commit comments

Comments
 (0)