diff --git a/aws/internal/service/efs/finder/finder.go b/aws/internal/service/efs/finder/finder.go index 9be4e9a73a8..415ec8a6d2c 100644 --- a/aws/internal/service/efs/finder/finder.go +++ b/aws/internal/service/efs/finder/finder.go @@ -8,6 +8,34 @@ import ( "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) +func FileSystemByID(conn *efs.EFS, id string) (*efs.FileSystemDescription, error) { + input := &efs.DescribeFileSystemsInput{ + FileSystemId: aws.String(id), + } + + output, err := conn.DescribeFileSystems(input) + + if tfawserr.ErrCodeEquals(err, efs.ErrCodeFileSystemNotFound) { + return nil, &resource.NotFoundError{ + LastError: err, + LastRequest: input, + } + } + + if err != nil { + return nil, err + } + + if output == nil || output.FileSystems == nil || len(output.FileSystems) == 0 || output.FileSystems[0] == nil { + return nil, &resource.NotFoundError{ + Message: "Empty result", + LastRequest: input, + } + } + + return output.FileSystems[0], nil +} + func BackupPolicyByID(conn *efs.EFS, id string) (*efs.BackupPolicy, error) { input := &efs.DescribeBackupPolicyInput{ FileSystemId: aws.String(id), diff --git a/aws/resource_aws_efs_file_system.go b/aws/resource_aws_efs_file_system.go index 7dd2922418c..25b0828d1eb 100644 --- a/aws/resource_aws_efs_file_system.go +++ b/aws/resource_aws_efs_file_system.go @@ -7,11 +7,14 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/efs" + "github.com/hashicorp/aws-sdk-go-base/tfawserr" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/terraform-providers/terraform-provider-aws/aws/internal/keyvaluetags" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/finder" "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/waiter" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func resourceAwsEfsFileSystem() *schema.Resource { @@ -112,9 +115,14 @@ func resourceAwsEfsFileSystem() *schema.Resource { Schema: map[string]*schema.Schema{ "transition_to_ia": { Type: schema.TypeString, - Required: true, + Optional: true, ValidateFunc: validation.StringInSlice(efs.TransitionToIARules_Values(), false), }, + "transition_to_primary_storage_class": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(efs.TransitionToPrimaryStorageClassRules_Values(), false), + }, }, }, }, @@ -279,35 +287,17 @@ func resourceAwsEfsFileSystemRead(d *schema.ResourceData, meta interface{}) erro defaultTagsConfig := meta.(*AWSClient).DefaultTagsConfig ignoreTagsConfig := meta.(*AWSClient).IgnoreTagsConfig - resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{ - FileSystemId: aws.String(d.Id()), - }) - if err != nil { - if isAWSErr(err, efs.ErrCodeFileSystemNotFound, "") { - log.Printf("[WARN] EFS file system (%s) could not be found.", d.Id()) - d.SetId("") - return nil - } - return err - } - - if hasEmptyFileSystems(resp) { - return fmt.Errorf("EFS file system %q could not be found.", d.Id()) - } - - var fs *efs.FileSystemDescription - for _, f := range resp.FileSystems { - if d.Id() == *f.FileSystemId { - fs = f - break - } - } - if fs == nil { - log.Printf("[WARN] EFS File System (%s) not found, removing from state", d.Id()) + fs, err := finder.FileSystemByID(conn, d.Id()) + if !d.IsNewResource() && tfresource.NotFound(err) { + log.Printf("[WARN] EFS file system (%s) not found, removing from state", d.Id()) d.SetId("") return nil } + if err != nil { + return fmt.Errorf("error reading EFS file system (%s): %w", d.Id(), err) + } + d.Set("arn", fs.FileSystemArn) d.Set("availability_zone_id", fs.AvailabilityZoneId) d.Set("availability_zone_name", fs.AvailabilityZoneName) @@ -359,14 +349,14 @@ func resourceAwsEfsFileSystemDelete(d *schema.ResourceData, meta interface{}) er FileSystemId: aws.String(d.Id()), }) if err != nil { - if isAWSErr(err, efs.ErrCodeFileSystemNotFound, "") { + if tfawserr.ErrCodeEquals(err, efs.ErrCodeFileSystemNotFound) { return nil } return fmt.Errorf("Error delete file system: %s with err %s", d.Id(), err.Error()) } if _, err := waiter.FileSystemDeleted(conn, d.Id()); err != nil { - if isAWSErr(err, efs.ErrCodeFileSystemNotFound, "") { + if tfawserr.ErrCodeEquals(err, efs.ErrCodeFileSystemNotFound) { return nil } return fmt.Errorf("error waiting for EFS file system (%s) deletion: %w", d.Id(), err) @@ -375,13 +365,6 @@ func resourceAwsEfsFileSystemDelete(d *schema.ResourceData, meta interface{}) er return nil } -func hasEmptyFileSystems(fs *efs.DescribeFileSystemsOutput) bool { - if fs != nil && len(fs.FileSystems) > 0 { - return false - } - return true -} - func flattenEfsFileSystemLifecyclePolicies(apiObjects []*efs.LifecyclePolicy) []interface{} { var tfList []interface{} @@ -396,6 +379,10 @@ func flattenEfsFileSystemLifecyclePolicies(apiObjects []*efs.LifecyclePolicy) [] tfMap["transition_to_ia"] = aws.StringValue(apiObject.TransitionToIA) } + if apiObject.TransitionToPrimaryStorageClass != nil { + tfMap["transition_to_primary_storage_class"] = aws.StringValue(apiObject.TransitionToPrimaryStorageClass) + } + tfList = append(tfList, tfMap) } @@ -418,6 +405,10 @@ func expandEfsFileSystemLifecyclePolicies(tfList []interface{}) []*efs.Lifecycle apiObject.TransitionToIA = aws.String(v) } + if v, ok := tfMap["transition_to_primary_storage_class"].(string); ok && v != "" { + apiObject.TransitionToPrimaryStorageClass = aws.String(v) + } + apiObjects = append(apiObjects, apiObject) } diff --git a/aws/resource_aws_efs_file_system_test.go b/aws/resource_aws_efs_file_system_test.go index 5e39705a772..693eb5bf784 100644 --- a/aws/resource_aws_efs_file_system_test.go +++ b/aws/resource_aws_efs_file_system_test.go @@ -12,6 +12,8 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/finder" + "github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource" ) func init() { @@ -60,26 +62,6 @@ func testSweepEfsFileSystems(region string) error { return sweeperErrs.ErrorOrNil() } -func TestResourceAWSEFSFileSystem_hasEmptyFileSystems(t *testing.T) { - fs := &efs.DescribeFileSystemsOutput{ - FileSystems: []*efs.FileSystemDescription{}, - } - - actual := hasEmptyFileSystems(fs) - if !actual { - t.Fatalf("Expected return value to be true, got %t", actual) - } - - // Add an empty file system. - fs.FileSystems = append(fs.FileSystems, &efs.FileSystemDescription{}) - - actual = hasEmptyFileSystems(fs) - if actual { - t.Fatalf("Expected return value to be false, got %t", actual) - } - -} - func TestAccAWSEFSFileSystem_basic(t *testing.T) { var desc efs.FileSystemDescription resourceName := "aws_efs_file_system.test" @@ -101,6 +83,7 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) { testAccCheckEfsFileSystem(resourceName, &desc), testAccCheckEfsFileSystemPerformanceMode(resourceName, "generalPurpose"), resource.TestCheckResourceAttr(resourceName, "tags.%", "0"), + resource.TestCheckResourceAttr(resourceName, "lifecycle_policy.#", "0"), resource.TestCheckResourceAttr(resourceName, "size_in_bytes.#", "1"), resource.TestCheckResourceAttrSet(resourceName, "size_in_bytes.0.value"), resource.TestCheckResourceAttrSet(resourceName, "size_in_bytes.0.value_in_ia"), @@ -382,18 +365,7 @@ func TestAccAWSEFSFileSystem_lifecyclePolicy(t *testing.T) { ), Check: resource.ComposeTestCheckFunc( testAccCheckEfsFileSystem(resourceName, &desc), - testAccCheckEfsFileSystemLifecyclePolicy(resourceName, "badExpectation"), - ), - ExpectError: regexp.MustCompile(`Expected: badExpectation`), - }, - { - Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( - "transition_to_ia", - efs.TransitionToIARulesAfter30Days, - ), - Check: resource.ComposeTestCheckFunc( - testAccCheckEfsFileSystem(resourceName, &desc), - testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter30Days), + resource.TestCheckResourceAttr(resourceName, "lifecycle_policy.0.transition_to_ia", efs.TransitionToIARulesAfter30Days), ), }, { @@ -401,72 +373,22 @@ func TestAccAWSEFSFileSystem_lifecyclePolicy(t *testing.T) { ImportState: true, ImportStateVerify: true, }, - }, - }) -} - -func TestAccAWSEFSFileSystem_lifecyclePolicy_update(t *testing.T) { - var desc efs.FileSystemDescription - resourceName := "aws_efs_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, efs.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckEfsFileSystemDestroy, - Steps: []resource.TestStep{ { - Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy("transition_to_ia", efs.TransitionToIARulesAfter30Days), - Check: resource.ComposeTestCheckFunc( - testAccCheckEfsFileSystem(resourceName, &desc), - testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter30Days), + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_primary_storage_class", + efs.TransitionToPrimaryStorageClassRulesAfter1Access, ), - }, - { - Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy("transition_to_ia", efs.TransitionToIARulesAfter90Days), Check: resource.ComposeTestCheckFunc( testAccCheckEfsFileSystem(resourceName, &desc), - testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter90Days), - ), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, - }, - }, - }) -} - -func TestAccAWSEFSFileSystem_lifecyclePolicy_removal(t *testing.T) { - var desc efs.FileSystemDescription - resourceName := "aws_efs_file_system.test" - - resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { testAccPreCheck(t) }, - ErrorCheck: testAccErrorCheck(t, efs.EndpointsID), - Providers: testAccProviders, - CheckDestroy: testAccCheckEfsFileSystemDestroy, - Steps: []resource.TestStep{ - { - Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy("transition_to_ia", efs.TransitionToIARulesAfter14Days), - Check: resource.ComposeTestCheckFunc( - testAccCheckEfsFileSystem(resourceName, &desc), - testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter14Days), + resource.TestCheckResourceAttr(resourceName, "lifecycle_policy.0.transition_to_primary_storage_class", efs.TransitionToPrimaryStorageClassRulesAfter1Access), ), }, { Config: testAccAWSEFSFileSystemConfigRemovedLifecyclePolicy, Check: resource.ComposeTestCheckFunc( testAccCheckEfsFileSystem(resourceName, &desc), - testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter14Days), + resource.TestCheckResourceAttr(resourceName, "lifecycle_policy.#", "0"), ), - ExpectError: regexp.MustCompile(fmt.Sprintf(`Expected: %s`, efs.TransitionToIARulesAfter14Days)), - }, - { - ResourceName: resourceName, - ImportState: true, - ImportStateVerify: true, }, }, }) @@ -488,6 +410,7 @@ func TestAccAWSEFSFileSystem_disappears(t *testing.T) { Check: resource.ComposeTestCheckFunc( testAccCheckEfsFileSystem(resourceName, &desc), testAccCheckResourceDisappears(testAccProvider, resourceAwsEfsFileSystem(), resourceName), + testAccCheckResourceDisappears(testAccProvider, resourceAwsEfsFileSystem(), resourceName), ), ExpectNonEmptyPlan: true, }, @@ -502,19 +425,17 @@ func testAccCheckEfsFileSystemDestroy(s *terraform.State) error { continue } - resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{ - FileSystemId: aws.String(rs.Primary.ID), - }) - if err != nil { - if isAWSErr(err, efs.ErrCodeFileSystemNotFound, "") { - // gone - return nil - } - return fmt.Errorf("Error describing EFS in tests: %s", err) + _, err := finder.FileSystemByID(conn, rs.Primary.ID) + + if tfresource.NotFound(err) { + continue } - if len(resp.FileSystems) > 0 { - return fmt.Errorf("EFS file system %q still exists", rs.Primary.ID) + + if err != nil { + return err } + + return fmt.Errorf("EFS file system %s still exists", rs.Primary.ID) } return nil @@ -532,19 +453,13 @@ func testAccCheckEfsFileSystem(resourceID string, fDesc *efs.FileSystemDescripti } conn := testAccProvider.Meta().(*AWSClient).efsconn - fs, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{ - FileSystemId: aws.String(rs.Primary.ID), - }) + fs, err := finder.FileSystemByID(conn, rs.Primary.ID) if err != nil { return err } - if len(fs.FileSystems) == 0 { - return fmt.Errorf("EFS File System not found") - } - - *fDesc = *fs.FileSystems[0] + *fDesc = *fs return nil } @@ -606,50 +521,6 @@ func testAccCheckEfsFileSystemPerformanceMode(resourceID string, expectedMode st } } -func testAccCheckEfsFileSystemLifecyclePolicy(resourceID string, expectedVal string) resource.TestCheckFunc { - return func(s *terraform.State) error { - rs, ok := s.RootModule().Resources[resourceID] - if !ok { - return fmt.Errorf("Not found: %s", resourceID) - } - - if rs.Primary.ID == "" { - return fmt.Errorf("No ID is set") - } - - conn := testAccProvider.Meta().(*AWSClient).efsconn - resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{ - FileSystemId: aws.String(rs.Primary.ID), - }) - if err != nil { - return fmt.Errorf("Error describing EFS file systems: %s", err.Error()) - } - - fs := resp.FileSystems[0] - - res, err := conn.DescribeLifecycleConfiguration(&efs.DescribeLifecycleConfigurationInput{ - FileSystemId: fs.FileSystemId, - }) - if err != nil { - return fmt.Errorf("Error describing lifecycle policy for EFS file system (%s): %s", - aws.StringValue(fs.FileSystemId), err.Error()) - } - lp := res.LifecyclePolicies - - newLP := make([]*map[string]interface{}, len(lp)) - - for i := 0; i < len(lp); i++ { - config := lp[i] - data := make(map[string]interface{}) - newLP[i] = &data - if *config.TransitionToIA == expectedVal { - return nil - } - } - return fmt.Errorf("Lifecycle Policy mismatch.\nExpected: %s\nFound: %+v", expectedVal, lp) - } -} - func testAccAWSEFSFileSystemConfig(rName string) string { return fmt.Sprintf(` resource "aws_efs_file_system" "test" {