Skip to content

Commit

Permalink
Merge pull request #33245 from hashicorp/f-aws_fsx_lustre_file_system…
Browse files Browse the repository at this point in the history
…-additions

FSx OpenZFS acceptance tests fixes and enhancements
  • Loading branch information
ewbankkit authored Aug 31, 2023
2 parents aef96a2 + 52487bb commit db5d714
Show file tree
Hide file tree
Showing 14 changed files with 1,731 additions and 1,225 deletions.
7 changes: 7 additions & 0 deletions .changelog/33245.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
```release-note:enhancement
resource/aws_fsx_openzfs_file_system: Add `endpoint_ip_address_range`, `preferred_subnet_id` and `route_table_ids` arguments to support the [Multi-AZ deployment type](https://docs.aws.amazon.com/fsx/latest/OpenZFSGuide/availability-durability.html#choosing-single-or-multi)
```

```release-note:bug
resource/aws_fsx_openzfs_file_system: Wait for administrative action completion when updating root volume
```
226 changes: 181 additions & 45 deletions internal/service/fsx/data_repository_association.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package fsx

import (
"context"
"errors"
"log"
"time"

Expand All @@ -15,6 +16,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/customdiff"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/id"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
Expand All @@ -34,6 +36,7 @@ func ResourceDataRepositoryAssociation() *schema.Resource {
ReadWithoutTimeout: resourceDataRepositoryAssociationRead,
UpdateWithoutTimeout: resourceDataRepositoryAssociationUpdate,
DeleteWithoutTimeout: resourceDataRepositoryAssociationDelete,

Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
},
Expand Down Expand Up @@ -67,6 +70,11 @@ func ResourceDataRepositoryAssociation() *schema.Resource {
validation.StringMatch(regexache.MustCompile(`^s3://`), "must begin with s3://"),
),
},
"delete_data_in_filesystem": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
"file_system_id": {
Type: schema.TypeString,
ForceNew: true,
Expand Down Expand Up @@ -142,11 +150,6 @@ func ResourceDataRepositoryAssociation() *schema.Resource {
},
DiffSuppressFunc: verify.SuppressMissingOptionalConfigurationBlock,
},
"delete_data_in_filesystem": {
Type: schema.TypeBool,
Optional: true,
Default: false,
},
names.AttrTags: tftags.TagsSchema(),
names.AttrTagsAll: tftags.TagsSchemaComputed(),
},
Expand Down Expand Up @@ -184,44 +187,13 @@ func resourceDataRepositoryAssociationCreate(ctx context.Context, d *schema.Reso
result, err := conn.CreateDataRepositoryAssociationWithContext(ctx, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "creating FSx Lustre Data Repository Association: %s", err)
return sdkdiag.AppendErrorf(diags, "creating FSx for Lustre Data Repository Association: %s", err)
}

d.SetId(aws.StringValue(result.Association.AssociationId))

if _, err := waitDataRepositoryAssociationCreated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutCreate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for FSx Lustre Data Repository Association (%s) create: %s", d.Id(), err)
}

return append(diags, resourceDataRepositoryAssociationRead(ctx, d, meta)...)
}

func resourceDataRepositoryAssociationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).FSxConn(ctx)

if d.HasChangesExcept("tags_all", "tags") {
input := &fsx.UpdateDataRepositoryAssociationInput{
ClientRequestToken: aws.String(id.UniqueId()),
AssociationId: aws.String(d.Id()),
}

if d.HasChange("imported_file_chunk_size") {
input.ImportedFileChunkSize = aws.Int64(int64(d.Get("imported_file_chunk_size").(int)))
}

if d.HasChange("s3") {
input.S3 = expandDataRepositoryAssociationS3(d.Get("s3").([]interface{}))
}

_, err := conn.UpdateDataRepositoryAssociationWithContext(ctx, input)
if err != nil {
return sdkdiag.AppendErrorf(diags, "updating FSX Lustre Data Repository Association (%s): %s", d.Id(), err)
}

if _, err := waitDataRepositoryAssociationUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for FSx Lustre Data Repository Association (%s) update: %s", d.Id(), err)
}
return sdkdiag.AppendErrorf(diags, "waiting for FSx for Lustre Data Repository Association (%s) create: %s", d.Id(), err)
}

return append(diags, resourceDataRepositoryAssociationRead(ctx, d, meta)...)
Expand All @@ -232,14 +204,15 @@ func resourceDataRepositoryAssociationRead(ctx context.Context, d *schema.Resour
conn := meta.(*conns.AWSClient).FSxConn(ctx)

association, err := FindDataRepositoryAssociationByID(ctx, conn, d.Id())

if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] FSx Lustre Data Repository Association (%s) not found, removing from state", d.Id())
log.Printf("[WARN] FSx for Lustre Data Repository Association (%s) not found, removing from state", d.Id())
d.SetId("")
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "reading FSx Lustre Data Repository Association (%s): %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "reading FSx for Lustre Data Repository Association (%s): %s", d.Id(), err)
}

d.Set("arn", association.ResourceARN)
Expand All @@ -249,37 +222,69 @@ func resourceDataRepositoryAssociationRead(ctx context.Context, d *schema.Resour
d.Set("file_system_path", association.FileSystemPath)
d.Set("imported_file_chunk_size", association.ImportedFileChunkSize)
if err := d.Set("s3", flattenDataRepositoryAssociationS3(association.S3)); err != nil {
return sdkdiag.AppendErrorf(diags, "setting s3 data repository configuration: %s", err)
return sdkdiag.AppendErrorf(diags, "setting s3: %s", err)
}

setTagsOut(ctx, association.Tags)

return diags
}

func resourceDataRepositoryAssociationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).FSxConn(ctx)

if d.HasChangesExcept("tags", "tags_all") {
input := &fsx.UpdateDataRepositoryAssociationInput{
AssociationId: aws.String(d.Id()),
ClientRequestToken: aws.String(id.UniqueId()),
}

if d.HasChange("imported_file_chunk_size") {
input.ImportedFileChunkSize = aws.Int64(int64(d.Get("imported_file_chunk_size").(int)))
}

if d.HasChange("s3") {
input.S3 = expandDataRepositoryAssociationS3(d.Get("s3").([]interface{}))
}

_, err := conn.UpdateDataRepositoryAssociationWithContext(ctx, input)

if err != nil {
return sdkdiag.AppendErrorf(diags, "updating FSx for Lustre Data Repository Association (%s): %s", d.Id(), err)
}

if _, err := waitDataRepositoryAssociationUpdated(ctx, conn, d.Id(), d.Timeout(schema.TimeoutUpdate)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for FSx for Lustre Data Repository Association (%s) update: %s", d.Id(), err)
}
}

return append(diags, resourceDataRepositoryAssociationRead(ctx, d, meta)...)
}

func resourceDataRepositoryAssociationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
var diags diag.Diagnostics
conn := meta.(*conns.AWSClient).FSxConn(ctx)

request := &fsx.DeleteDataRepositoryAssociationInput{
ClientRequestToken: aws.String(id.UniqueId()),
AssociationId: aws.String(d.Id()),
ClientRequestToken: aws.String(id.UniqueId()),
DeleteDataInFileSystem: aws.Bool(d.Get("delete_data_in_filesystem").(bool)),
}

log.Printf("[DEBUG] Deleting FSx Lustre Data Repository Association: %s", d.Id())
log.Printf("[DEBUG] Deleting FSx for Lustre Data Repository Association: %s", d.Id())
_, err := conn.DeleteDataRepositoryAssociationWithContext(ctx, request)

if tfawserr.ErrCodeEquals(err, fsx.ErrCodeDataRepositoryAssociationNotFound) {
return diags
}

if err != nil {
return sdkdiag.AppendErrorf(diags, "deleting FSx Lustre Data Repository Association (%s): %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "deleting FSx for Lustre Data Repository Association (%s): %s", d.Id(), err)
}

if _, err := waitDataRepositoryAssociationDeleted(ctx, conn, d.Id(), d.Timeout(schema.TimeoutDelete)); err != nil {
return sdkdiag.AppendErrorf(diags, "waiting for FSx Lustre Data Repository Association (%s) to deleted: %s", d.Id(), err)
return sdkdiag.AppendErrorf(diags, "waiting for FSx for Lustre Data Repository Association (%s) delete: %s", d.Id(), err)
}

return diags
Expand Down Expand Up @@ -375,3 +380,134 @@ func flattenS3AutoImportPolicy(policy *fsx.AutoImportPolicy) []map[string][]inte

return []map[string][]interface{}{result}
}

func FindDataRepositoryAssociationByID(ctx context.Context, conn *fsx.FSx, id string) (*fsx.DataRepositoryAssociation, error) {
input := &fsx.DescribeDataRepositoryAssociationsInput{
AssociationIds: aws.StringSlice([]string{id}),
}

return findDataRepositoryAssociation(ctx, conn, input)
}

func findDataRepositoryAssociation(ctx context.Context, conn *fsx.FSx, input *fsx.DescribeDataRepositoryAssociationsInput) (*fsx.DataRepositoryAssociation, error) {
output, err := findDataRepositoryAssociations(ctx, conn, input)

if err != nil {
return nil, err
}

return tfresource.AssertSinglePtrResult(output)
}

func findDataRepositoryAssociations(ctx context.Context, conn *fsx.FSx, input *fsx.DescribeDataRepositoryAssociationsInput) ([]*fsx.DataRepositoryAssociation, error) {
var output []*fsx.DataRepositoryAssociation

err := conn.DescribeDataRepositoryAssociationsPagesWithContext(ctx, input, func(page *fsx.DescribeDataRepositoryAssociationsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, v := range page.Associations {
if v != nil {
output = append(output, v)
}
}

return !lastPage
})

if tfawserr.ErrCodeEquals(err, fsx.ErrCodeDataRepositoryAssociationNotFound) {
return nil, &retry.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

return output, nil
}

func statusDataRepositoryAssociation(ctx context.Context, conn *fsx.FSx, id string) retry.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := FindDataRepositoryAssociationByID(ctx, conn, id)

if tfresource.NotFound(err) {
return nil, "", nil
}

if err != nil {
return nil, "", err
}

return output, aws.StringValue(output.Lifecycle), nil
}
}

func waitDataRepositoryAssociationCreated(ctx context.Context, conn *fsx.FSx, id string, timeout time.Duration) (*fsx.DataRepositoryAssociation, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{fsx.DataRepositoryLifecycleCreating},
Target: []string{fsx.DataRepositoryLifecycleAvailable},
Refresh: statusDataRepositoryAssociation(ctx, conn, id),
Timeout: timeout,
Delay: 30 * time.Second,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*fsx.DataRepositoryAssociation); ok {
if status, details := aws.StringValue(output.Lifecycle), output.FailureDetails; status == fsx.DataRepositoryLifecycleFailed && details != nil {
tfresource.SetLastError(err, errors.New(aws.StringValue(output.FailureDetails.Message)))
}

return output, err
}

return nil, err
}

func waitDataRepositoryAssociationUpdated(ctx context.Context, conn *fsx.FSx, id string, timeout time.Duration) (*fsx.DataRepositoryAssociation, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{fsx.DataRepositoryLifecycleUpdating},
Target: []string{fsx.DataRepositoryLifecycleAvailable},
Refresh: statusDataRepositoryAssociation(ctx, conn, id),
Timeout: timeout,
Delay: 30 * time.Second,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*fsx.DataRepositoryAssociation); ok {
if status, details := aws.StringValue(output.Lifecycle), output.FailureDetails; status == fsx.DataRepositoryLifecycleFailed && details != nil {
tfresource.SetLastError(err, errors.New(aws.StringValue(output.FailureDetails.Message)))
}

return output, err
}

return nil, err
}

func waitDataRepositoryAssociationDeleted(ctx context.Context, conn *fsx.FSx, id string, timeout time.Duration) (*fsx.DataRepositoryAssociation, error) {
stateConf := &retry.StateChangeConf{
Pending: []string{fsx.DataRepositoryLifecycleAvailable, fsx.DataRepositoryLifecycleDeleting},
Target: []string{},
Refresh: statusDataRepositoryAssociation(ctx, conn, id),
Timeout: timeout,
Delay: 30 * time.Second,
}

outputRaw, err := stateConf.WaitForStateContext(ctx)

if output, ok := outputRaw.(*fsx.DataRepositoryAssociation); ok {
if status, details := aws.StringValue(output.Lifecycle), output.FailureDetails; status == fsx.DataRepositoryLifecycleFailed && details != nil {
tfresource.SetLastError(err, errors.New(aws.StringValue(output.FailureDetails.Message)))
}

return output, err
}

return nil, err
}
Loading

0 comments on commit db5d714

Please sign in to comment.