diff --git a/internal/create/errors.go b/internal/create/errors.go index 001a907638a6..d1f52c0bc1a1 100644 --- a/internal/create/errors.go +++ b/internal/create/errors.go @@ -49,14 +49,24 @@ func Error(service, action, resource, id string, gotError error) error { return errors.New(ProblemStandardMessage(service, action, resource, id, gotError)) } +// AddError returns diag.Diagnostics with an additional diag.Diagnostic containing +// an error using a standardized problem message +func AddError(diags diag.Diagnostics, service, action, resource, id string, gotError error) diag.Diagnostics { + return append(diags, newError(service, action, resource, id, gotError)) +} + // DiagError returns a 1-length diag.Diagnostics with a diag.Error-level diag.Diagnostic // with a standardized error message func DiagError(service, action, resource, id string, gotError error) diag.Diagnostics { return diag.Diagnostics{ - diag.Diagnostic{ - Severity: diag.Error, - Summary: ProblemStandardMessage(service, action, resource, id, gotError), - }, + newError(service, action, resource, id, gotError), + } +} + +func newError(service, action, resource, id string, gotError error) diag.Diagnostic { + return diag.Diagnostic{ + Severity: diag.Error, + Summary: ProblemStandardMessage(service, action, resource, id, gotError), } } @@ -99,6 +109,15 @@ func AddWarning(diags diag.Diagnostics, service, action, resource, id string, go ) } +func AddWarningMessage(diags diag.Diagnostics, service, action, resource, id, message string) diag.Diagnostics { + return append(diags, + diag.Diagnostic{ + Severity: diag.Warning, + Summary: ProblemStandardMessage(service, action, resource, id, fmt.Errorf(message)), + }, + ) +} + // AddWarningNotFoundRemoveState returns diag.Diagnostics with an additional diag.Diagnostic containing // a warning using a standardized problem message func AddWarningNotFoundRemoveState(service, action, resource, id string) diag.Diagnostics { diff --git a/internal/service/lakeformation/lakeformation_test.go b/internal/service/lakeformation/lakeformation_test.go index 6f0966caaca5..17d3cc7b6678 100644 --- a/internal/service/lakeformation/lakeformation_test.go +++ b/internal/service/lakeformation/lakeformation_test.go @@ -62,11 +62,13 @@ func TestAccLakeFormation_serial(t *testing.T) { "valuesOverFifty": testAccLFTag_Values_overFifty, }, "ResourceLFTags": { - "basic": testAccResourceLFTags_basic, - "database": testAccResourceLFTags_database, - "databaseMultiple": testAccResourceLFTags_databaseMultiple, - "table": testAccResourceLFTags_table, - "tableWithColumns": testAccResourceLFTags_tableWithColumns, + "basic": testAccResourceLFTags_basic, + "database": testAccResourceLFTags_database, + "databaseMultipleTags": testAccResourceLFTags_databaseMultipleTags, + "disappears": testAccResourceLFTags_disappears, + "hierarchy": testAccResourceLFTags_hierarchy, + "table": testAccResourceLFTags_table, + "tableWithColumns": testAccResourceLFTags_tableWithColumns, }, } diff --git a/internal/service/lakeformation/resource_lf_tags.go b/internal/service/lakeformation/resource_lf_tags.go index 0f5d45cc4506..95104ff1980a 100644 --- a/internal/service/lakeformation/resource_lf_tags.go +++ b/internal/service/lakeformation/resource_lf_tags.go @@ -4,7 +4,6 @@ import ( "bytes" "context" "fmt" - "log" "reflect" "time" @@ -18,6 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-aws/internal/conns" "github.com/hashicorp/terraform-provider-aws/internal/create" + "github.com/hashicorp/terraform-provider-aws/internal/errs" "github.com/hashicorp/terraform-provider-aws/internal/tfresource" "github.com/hashicorp/terraform-provider-aws/internal/verify" "github.com/hashicorp/terraform-provider-aws/names" @@ -223,31 +223,27 @@ func ResourceResourceLFTags() *schema.Resource { } func resourceResourceLFTagsCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).LakeFormationConn(ctx) - input := &lakeformation.AddLFTagsToResourceInput{ - Resource: &lakeformation.Resource{}, - } + input := &lakeformation.AddLFTagsToResourceInput{} if v, ok := d.GetOk("catalog_id"); ok { input.CatalogId = aws.String(v.(string)) } - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - if v, ok := d.GetOk("lf_tag"); ok && v.(*schema.Set).Len() > 0 { input.LFTags = expandLFTagPairs(v.(*schema.Set).List()) } - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{})) + tagger, ds := lfTagsTagger(d) + diags = append(diags, ds...) + if diags.HasError() { + return diags } - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.TableWithColumns = expandTableColumnsResource(v.([]interface{})[0].(map[string]interface{})) - } + input.Resource = tagger.ExpandResource(d) var output *lakeformation.AddLFTagsToResourceOutput err := retry.RetryContext(ctx, IAMPropagationTimeout, func() *retry.RetryError { @@ -271,19 +267,16 @@ func resourceResourceLFTagsCreate(ctx context.Context, d *schema.ResourceData, m } if err != nil { - return create.DiagError(names.LakeFormation, create.ErrActionCreating, ResNameLFTags, input.String(), err) + return create.AddError(diags, names.LakeFormation, create.ErrActionCreating, ResNameLFTags, input.String(), err) } - diags := diag.Diagnostics{} - if output != nil && len(output.Failures) > 0 { for _, v := range output.Failures { if v.LFTag == nil || v.Error == nil { continue } - diags = create.AddWarning( - diags, + diags = create.AddError(diags, names.LakeFormation, create.ErrActionCreating, ResNameLFTags, @@ -291,27 +284,22 @@ func resourceResourceLFTagsCreate(ctx context.Context, d *schema.ResourceData, m awserr.New(aws.StringValue(v.Error.ErrorCode), aws.StringValue(v.Error.ErrorMessage), nil), ) } - - if len(diags) == len(input.LFTags) { - return append(diags, - diag.Diagnostic{ - Severity: diag.Error, - Summary: create.ProblemStandardMessage(names.LakeFormation, create.ErrActionCreating, ResNameLFTags, "", fmt.Errorf("attempted to add %d tags, %d failures", len(input.LFTags), len(diags))), - }, - ) - } + } + if diags.HasError() { + return diags } d.SetId(fmt.Sprintf("%d", create.StringHashcode(input.String()))) - return append(resourceResourceLFTagsRead(ctx, d, meta), diags...) + return append(diags, resourceResourceLFTagsRead(ctx, d, meta)...) } func resourceResourceLFTagsRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).LakeFormationConn(ctx) input := &lakeformation.GetResourceLFTagsInput{ - Resource: &lakeformation.Resource{}, ShowAssignedLFTags: aws.Bool(true), } @@ -319,82 +307,53 @@ func resourceResourceLFTagsRead(ctx context.Context, d *schema.ResourceData, met input.CatalogId = aws.String(v.(string)) } - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{})) + tagger, ds := lfTagsTagger(d) + diags = append(diags, ds...) + if diags.HasError() { + return diags } - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.TableWithColumns = expandTableColumnsResource(v.([]interface{})[0].(map[string]interface{})) - } + input.Resource = tagger.ExpandResource(d) output, err := conn.GetResourceLFTagsWithContext(ctx, input) if err != nil { - return create.DiagError(names.LakeFormation, create.ErrActionReading, ResNameLFTags, d.Id(), err) - } - - if len(output.LFTagOnDatabase) > 0 { - if err := d.Set("lf_tag", flattenLFTagPairs(output.LFTagOnDatabase)); err != nil { - return create.DiagError(names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err) - } - } - - if len(output.LFTagsOnColumns) > 0 { - for _, v := range output.LFTagsOnColumns { - if aws.StringValue(v.Name) != d.Get("table_with_columns.0.name").(string) { - continue - } - - if err := d.Set("lf_tag", flattenLFTagPairs(v.LFTags)); err != nil { - return create.DiagError(names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err) - } - } + return create.AddError(diags, names.LakeFormation, create.ErrActionReading, ResNameLFTags, d.Id(), err) } - if len(output.LFTagsOnTable) > 0 { - if err := d.Set("lf_tag", flattenLFTagPairs(output.LFTagsOnTable)); err != nil { - return create.DiagError(names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err) - } + if err := d.Set("lf_tag", tagger.FlattenTags(output)); err != nil { + return create.AddError(diags, names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), err) } - return nil + return diags } func resourceResourceLFTagsDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var diags diag.Diagnostics + conn := meta.(*conns.AWSClient).LakeFormationConn(ctx) - input := &lakeformation.RemoveLFTagsFromResourceInput{ - Resource: &lakeformation.Resource{}, - } + input := &lakeformation.RemoveLFTagsFromResourceInput{} if v, ok := d.GetOk("catalog_id"); ok { input.CatalogId = aws.String(v.(string)) } - if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Database = ExpandDatabaseResource(v.([]interface{})[0].(map[string]interface{})) - } - if v, ok := d.GetOk("lf_tag"); ok && v.(*schema.Set).Len() > 0 { input.LFTags = expandLFTagPairs(v.(*schema.Set).List()) } - if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.Table = ExpandTableResource(v.([]interface{})[0].(map[string]interface{})) + tagger, ds := lfTagsTagger(d) + diags = append(diags, ds...) + if diags.HasError() { + return diags } - if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { - input.Resource.TableWithColumns = expandTableColumnsResource(v.([]interface{})[0].(map[string]interface{})) - } + input.Resource = tagger.ExpandResource(d) - if input.Resource == nil || reflect.DeepEqual(input.Resource, &lakeformation.Resource{}) { + if input.Resource == nil || reflect.DeepEqual(input.Resource, &lakeformation.Resource{}) || len(input.LFTags) == 0 { // if resource is empty, don't delete = it won't delete anything since this is the predicate - log.Printf("[WARN] No Lake Formation Resource LF Tags to remove") - return nil + return create.AddWarningMessage(diags, names.LakeFormation, create.ErrActionSetting, ResNameLFTags, d.Id(), "no LF-Tags to remove") } err := retry.RetryContext(ctx, d.Timeout(schema.TimeoutDelete), func() *retry.RetryError { @@ -408,7 +367,7 @@ func resourceResourceLFTagsDelete(ctx context.Context, d *schema.ResourceData, m return retry.RetryableError(err) } - return retry.NonRetryableError(fmt.Errorf("unable to revoke Lake Formation Permissions: %w", err)) + return retry.NonRetryableError(fmt.Errorf("removing Lake Formation LF-Tags: %w", err)) } return nil }) @@ -418,10 +377,83 @@ func resourceResourceLFTagsDelete(ctx context.Context, d *schema.ResourceData, m } if err != nil { - return create.DiagError(names.LakeFormation, create.ErrActionDeleting, ResNameLFTags, d.Id(), err) + return create.AddError(diags, names.LakeFormation, create.ErrActionDeleting, ResNameLFTags, d.Id(), err) + } + + return diags +} + +func lfTagsTagger(d *schema.ResourceData) (tagger, diag.Diagnostics) { + var diags diag.Diagnostics + if v, ok := d.GetOk("database"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + return &databaseTagger{}, diags + } else if v, ok := d.GetOk("table"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + return &tableTagger{}, diags + } else if v, ok := d.GetOk("table_with_columns"); ok && len(v.([]interface{})) > 0 && v.([]interface{})[0] != nil { + return &columnTagger{}, diags + } else { + diags = append(diags, errs.NewErrorDiagnostic( + "Invalid Lake Formation Resource Type", + "An unexpected error occurred while resolving the Lake Formation Resource type. "+ + "This is always an error in the provider. "+ + "Please report the following to the provider developer:\n\n"+ + "No Lake Formation Resource defined.", + )) + return nil, diags + } +} + +type tagger interface { + ExpandResource(*schema.ResourceData) *lakeformation.Resource + FlattenTags(*lakeformation.GetResourceLFTagsOutput) []any +} + +type databaseTagger struct{} + +func (t *databaseTagger) ExpandResource(d *schema.ResourceData) *lakeformation.Resource { + v := d.Get("database").([]any)[0].(map[string]any) + return &lakeformation.Resource{ + Database: ExpandDatabaseResource(v), + } +} + +func (t *databaseTagger) FlattenTags(output *lakeformation.GetResourceLFTagsOutput) []any { + return flattenLFTagPairs(output.LFTagOnDatabase) +} + +type tableTagger struct{} + +func (t *tableTagger) ExpandResource(d *schema.ResourceData) *lakeformation.Resource { + v := d.Get("table").([]any)[0].(map[string]any) + return &lakeformation.Resource{ + Table: ExpandTableResource(v), + } +} + +func (t *tableTagger) FlattenTags(output *lakeformation.GetResourceLFTagsOutput) []any { + return flattenLFTagPairs(output.LFTagsOnTable) +} + +type columnTagger struct{} + +func (t *columnTagger) ExpandResource(d *schema.ResourceData) *lakeformation.Resource { + v := d.Get("table_with_columns").([]any)[0].(map[string]any) + return &lakeformation.Resource{ + TableWithColumns: expandTableColumnsResource(v), + } +} + +func (t *columnTagger) FlattenTags(output *lakeformation.GetResourceLFTagsOutput) []any { + if len(output.LFTagsOnColumns) == 0 { + return []any{} + } + + tags := output.LFTagsOnColumns[0] + if tags == nil { + return []any{} } - return nil + return flattenLFTagPairs(tags.LFTags) } func lfTagsHash(v interface{}) int { diff --git a/internal/service/lakeformation/resource_lf_tags_test.go b/internal/service/lakeformation/resource_lf_tags_test.go index a2f994457681..0e52ecb18bbf 100644 --- a/internal/service/lakeformation/resource_lf_tags_test.go +++ b/internal/service/lakeformation/resource_lf_tags_test.go @@ -15,6 +15,7 @@ import ( "github.com/hashicorp/terraform-plugin-testing/terraform" "github.com/hashicorp/terraform-provider-aws/internal/acctest" "github.com/hashicorp/terraform-provider-aws/internal/conns" + tflakeformation "github.com/hashicorp/terraform-provider-aws/internal/service/lakeformation" ) func testAccResourceLFTags_basic(t *testing.T) { @@ -44,6 +45,29 @@ func testAccResourceLFTags_basic(t *testing.T) { }) } +func testAccResourceLFTags_disappears(t *testing.T) { + ctx := acctest.Context(t) + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + resourceName := "aws_lakeformation_resource_lf_tags.test" + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, lakeformation.EndpointsID) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckResourceDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccResourceLFTagsConfig_basic(rName, []string{"copse"}, "copse"), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatabaseLFTagsExists(ctx, resourceName), + acctest.CheckResourceDisappears(ctx, acctest.Provider, tflakeformation.ResourceResourceLFTags(), resourceName), + ), + ExpectNonEmptyPlan: true, + }, + }, + }) +} + func testAccResourceLFTags_database(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_lakeformation_resource_lf_tags.test" @@ -80,7 +104,7 @@ func testAccResourceLFTags_database(t *testing.T) { }) } -func testAccResourceLFTags_databaseMultiple(t *testing.T) { +func testAccResourceLFTags_databaseMultipleTags(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_lakeformation_resource_lf_tags.test" rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) @@ -92,7 +116,7 @@ func testAccResourceLFTags_databaseMultiple(t *testing.T) { CheckDestroy: testAccCheckDatabaseLFTagsDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccResourceLFTagsConfig_databaseMultiple(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "woodcote", "theloop"), + Config: testAccResourceLFTagsConfig_databaseMultipleTags(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "woodcote", "theloop"), Destroy: false, Check: resource.ComposeTestCheckFunc( testAccCheckDatabaseLFTagsExists(ctx, resourceName), @@ -107,7 +131,7 @@ func testAccResourceLFTags_databaseMultiple(t *testing.T) { ), }, { - Config: testAccResourceLFTagsConfig_databaseMultiple(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "stowe", "becketts"), + Config: testAccResourceLFTagsConfig_databaseMultipleTags(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "stowe", "becketts"), Check: resource.ComposeTestCheckFunc( testAccCheckDatabaseLFTagsExists(ctx, resourceName), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "lf_tag.*", map[string]string{ @@ -124,6 +148,83 @@ func testAccResourceLFTags_databaseMultiple(t *testing.T) { }) } +func testAccResourceLFTags_hierarchy(t *testing.T) { + ctx := acctest.Context(t) + databaseResourceName := "aws_lakeformation_resource_lf_tags.database_tags" + tableResourceName := "aws_lakeformation_resource_lf_tags.table_tags" + columnResourceName := "aws_lakeformation_resource_lf_tags.column_tags" + rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix) + + resource.Test(t, resource.TestCase{ + PreCheck: func() { acctest.PreCheck(ctx, t); acctest.PreCheckPartitionHasService(t, lakeformation.EndpointsID) }, + ErrorCheck: acctest.ErrorCheck(t, lakeformation.EndpointsID), + ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories, + CheckDestroy: testAccCheckDatabaseLFTagsDestroy(ctx), + Steps: []resource.TestStep{ + { + Config: testAccResourceLFTagsConfig_hierarchy(rName, + []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, + []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, + []string{"one", "two", "three"}, + "woodcote", + "theloop", + "two", + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatabaseLFTagsExists(ctx, databaseResourceName), + testAccCheckDatabaseLFTagsExists(ctx, tableResourceName), + testAccCheckDatabaseLFTagsExists(ctx, columnResourceName), + resource.TestCheckResourceAttr(databaseResourceName, "lf_tag.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(databaseResourceName, "lf_tag.*", map[string]string{ + "key": rName, + "value": "woodcote", + }), + resource.TestCheckResourceAttr(tableResourceName, "lf_tag.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(tableResourceName, "lf_tag.*", map[string]string{ + "key": fmt.Sprintf("%s-2", rName), + "value": "theloop", + }), + resource.TestCheckResourceAttr(columnResourceName, "lf_tag.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(columnResourceName, "lf_tag.*", map[string]string{ + "key": fmt.Sprintf("%s-3", rName), + "value": "two", + }), + ), + }, + { + Config: testAccResourceLFTagsConfig_hierarchy(rName, + []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, + []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, + []string{"one", "two", "three"}, + "stowe", + "becketts", + "three", + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckDatabaseLFTagsExists(ctx, databaseResourceName), + testAccCheckDatabaseLFTagsExists(ctx, tableResourceName), + testAccCheckDatabaseLFTagsExists(ctx, columnResourceName), + resource.TestCheckResourceAttr(databaseResourceName, "lf_tag.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(databaseResourceName, "lf_tag.*", map[string]string{ + "key": rName, + "value": "stowe", + }), + resource.TestCheckResourceAttr(tableResourceName, "lf_tag.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(tableResourceName, "lf_tag.*", map[string]string{ + "key": fmt.Sprintf("%s-2", rName), + "value": "becketts", + }), + resource.TestCheckResourceAttr(columnResourceName, "lf_tag.#", "1"), + resource.TestCheckTypeSetElemNestedAttrs(columnResourceName, "lf_tag.*", map[string]string{ + "key": fmt.Sprintf("%s-3", rName), + "value": "three", + }), + ), + }, + }, + }) +} + func testAccResourceLFTags_table(t *testing.T) { ctx := acctest.Context(t) resourceName := "aws_lakeformation_resource_lf_tags.test" @@ -172,7 +273,7 @@ func testAccResourceLFTags_tableWithColumns(t *testing.T) { CheckDestroy: testAccCheckDatabaseLFTagsDestroy(ctx), Steps: []resource.TestStep{ { - Config: testAccResourceLFTagsConfig_tableWithColumnsMultiple(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "luffield", "vale"), + Config: testAccResourceLFTagsConfig_tableWithColumnsMultipleTags(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "luffield", "vale"), Destroy: false, Check: resource.ComposeTestCheckFunc( testAccCheckDatabaseLFTagsExists(ctx, resourceName), @@ -187,7 +288,7 @@ func testAccResourceLFTags_tableWithColumns(t *testing.T) { ), }, { - Config: testAccResourceLFTagsConfig_tableWithColumnsMultiple(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "copse", "aintree"), + Config: testAccResourceLFTagsConfig_tableWithColumnsMultipleTags(rName, []string{"abbey", "village", "luffield", "woodcote", "copse", "chapel", "stowe", "club"}, []string{"farm", "theloop", "aintree", "brooklands", "maggotts", "becketts", "vale"}, "copse", "aintree"), Check: resource.ComposeTestCheckFunc( testAccCheckDatabaseLFTagsExists(ctx, resourceName), resource.TestCheckTypeSetElemNestedAttrs(resourceName, "lf_tag.*", map[string]string{ @@ -420,7 +521,7 @@ resource "aws_lakeformation_lf_tag" "test" { key = %[1]q values = [%[2]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -436,7 +537,7 @@ resource "aws_lakeformation_resource_lf_tags" "test" { value = %[3]q } - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } `, rName, fmt.Sprintf(`"%s"`, strings.Join(values, `", "`)), value) @@ -462,7 +563,7 @@ resource "aws_lakeformation_lf_tag" "test" { key = %[1]q values = [%[2]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -476,13 +577,13 @@ resource "aws_lakeformation_resource_lf_tags" "test" { value = %[3]q } - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } `, rName, fmt.Sprintf(`"%s"`, strings.Join(values, `", "`)), value) } -func testAccResourceLFTagsConfig_databaseMultiple(rName string, values1, values2 []string, value1, value2 string) string { +func testAccResourceLFTagsConfig_databaseMultipleTags(rName string, values1, values2 []string, value1, value2 string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -502,7 +603,7 @@ resource "aws_lakeformation_lf_tag" "test" { key = %[1]q values = [%[2]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -510,7 +611,7 @@ resource "aws_lakeformation_lf_tag" "test2" { key = "%[1]s-2" values = [%[3]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -529,12 +630,121 @@ resource "aws_lakeformation_resource_lf_tags" "test" { value = %[5]q } - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } `, rName, fmt.Sprintf(`"%s"`, strings.Join(values1, `", "`)), fmt.Sprintf(`"%s"`, strings.Join(values2, `", "`)), value1, value2) } +func testAccResourceLFTagsConfig_hierarchy(rName string, values1, values2, values3 []string, value1, value2, value3 string) string { + return fmt.Sprintf(` +data "aws_caller_identity" "current" {} + +data "aws_iam_session_context" "current" { + arn = data.aws_caller_identity.current.arn +} + +resource "aws_lakeformation_data_lake_settings" "test" { + admins = [data.aws_iam_session_context.current.issuer_arn] +} + +resource "aws_glue_catalog_database" "test" { + name = %[1]q +} + +resource "aws_glue_catalog_table" "test" { + name = %[1]q + database_name = aws_glue_catalog_database.test.name + + storage_descriptor { + columns { + name = "event" + type = "string" + } + + columns { + name = "timestamp" + type = "date" + } + + columns { + name = "value" + type = "double" + } + } +} + +resource "aws_lakeformation_lf_tag" "test" { + key = %[1]q + values = [%[2]s] + + # for consistency, ensure that admins are set up before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_lf_tag" "test2" { + key = "%[1]s-2" + values = [%[3]s] + + # for consistency, ensure that admins are set up before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_lf_tag" "column_tags" { + key = "%[1]s-3" + values = [%[6]s] + + # for consistency, ensure that admins are set up before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_resource_lf_tags" "database_tags" { + database { + name = aws_glue_catalog_database.test.name + } + + lf_tag { + key = aws_lakeformation_lf_tag.test.key + value = %[4]q + } + + # for consistency, ensure that admins are set up before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_resource_lf_tags" "table_tags" { + table { + database_name = aws_glue_catalog_database.test.name + name = aws_glue_catalog_table.test.name + } + + lf_tag { + key = aws_lakeformation_lf_tag.test2.key + value = %[5]q + } + + # for consistency, ensure that admins are set up before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} + +resource "aws_lakeformation_resource_lf_tags" "column_tags" { + table_with_columns { + database_name = aws_glue_catalog_database.test.name + name = aws_glue_catalog_table.test.name + column_names = ["event", "timestamp"] + } + + lf_tag { + key = aws_lakeformation_lf_tag.column_tags.key + value = %[7]q + } + + # for consistency, ensure that admins are set up before testing + depends_on = [aws_lakeformation_data_lake_settings.test] +} +`, rName, fmt.Sprintf(`"%s"`, strings.Join(values1, `", "`)), fmt.Sprintf(`"%s"`, strings.Join(values2, `", "`)), value1, value2, fmt.Sprintf(`"%s"`, strings.Join(values3, `", "`)), value3) +} + func testAccResourceLFTagsConfig_table(rName string, values []string, value string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -577,7 +787,7 @@ resource "aws_lakeformation_lf_tag" "test" { key = %[1]q values = [%[2]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -592,13 +802,13 @@ resource "aws_lakeformation_resource_lf_tags" "test" { value = %[3]q } - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } `, rName, fmt.Sprintf(`"%s"`, strings.Join(values, `", "`)), value) } -func testAccResourceLFTagsConfig_tableWithColumnsMultiple(rName string, values1, values2 []string, value1 string, value2 string) string { +func testAccResourceLFTagsConfig_tableWithColumnsMultipleTags(rName string, values1, values2 []string, value1 string, value2 string) string { return fmt.Sprintf(` data "aws_caller_identity" "current" {} @@ -640,7 +850,7 @@ resource "aws_lakeformation_lf_tag" "test" { key = %[1]q values = [%[2]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -648,7 +858,7 @@ resource "aws_lakeformation_lf_tag" "test2" { key = "%[1]s-2" values = [%[3]s] - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } @@ -669,7 +879,7 @@ resource "aws_lakeformation_resource_lf_tags" "test" { value = %[5]q } - # for consistency, ensure that admins are setup before testing + # for consistency, ensure that admins are set up before testing depends_on = [aws_lakeformation_data_lake_settings.test] } `, rName, fmt.Sprintf(`"%s"`, strings.Join(values1, `", "`)), fmt.Sprintf(`"%s"`, strings.Join(values2, `", "`)), value1, value2)