From 3a5475f463c1459e1dd6fc883fe008ec5a011f5e Mon Sep 17 00:00:00 2001 From: Ryan Kennedy Date: Mon, 5 Aug 2019 23:46:38 -0700 Subject: [PATCH] adding efs_file_system lifecycle_policy config block --- aws/resource_aws_efs_file_system.go | 89 ++++++++++ aws/resource_aws_efs_file_system_test.go | 176 ++++++++++++++++++- website/docs/r/efs_file_system.html.markdown | 20 +++ 3 files changed, 284 insertions(+), 1 deletion(-) diff --git a/aws/resource_aws_efs_file_system.go b/aws/resource_aws_efs_file_system.go index d2066cb6fb1..fc034a99cb2 100644 --- a/aws/resource_aws_efs_file_system.go +++ b/aws/resource_aws_efs_file_system.go @@ -31,6 +31,7 @@ func resourceAwsEfsFileSystem() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "creation_token": { Type: schema.TypeString, Optional: true, @@ -93,6 +94,26 @@ func resourceAwsEfsFileSystem() *schema.Resource { efs.ThroughputModeProvisioned, }, false), }, + + "lifecycle_policy": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "transition_to_ia": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{ + efs.TransitionToIARulesAfter14Days, + efs.TransitionToIARulesAfter30Days, + efs.TransitionToIARulesAfter60Days, + efs.TransitionToIARulesAfter90Days, + }, false), + }, + }, + }, + }, }, } } @@ -160,6 +181,18 @@ func resourceAwsEfsFileSystemCreate(d *schema.ResourceData, meta interface{}) er } log.Printf("[DEBUG] EFS file system %q created.", d.Id()) + _, hasLifecyclePolicy := d.GetOk("lifecycle_policy") + if hasLifecyclePolicy { + _, err := conn.PutLifecycleConfiguration(&efs.PutLifecycleConfigurationInput{ + FileSystemId: aws.String(d.Id()), + LifecyclePolicies: resourceAwsEfsFileSystemLifecyclePolicy(d.Get("lifecycle_policy").([]interface{})), + }) + if err != nil { + return fmt.Errorf("Error creating lifecycle policy for EFS file system %q: %s", + d.Id(), err.Error()) + } + } + err = setTagsEFS(conn, d) if err != nil { return fmt.Errorf("error setting tags for EFS file system (%q): %s", d.Id(), err) @@ -203,6 +236,17 @@ func resourceAwsEfsFileSystemUpdate(d *schema.ResourceData, meta interface{}) er } } + if d.HasChange("lifecycle_policy") { + _, err := conn.PutLifecycleConfiguration(&efs.PutLifecycleConfigurationInput{ + FileSystemId: aws.String(d.Id()), + LifecyclePolicies: resourceAwsEfsFileSystemLifecyclePolicy(d.Get("lifecycle_policy").([]interface{})), + }) + if err != nil { + return fmt.Errorf("Error updating lifecycle policy for EFS file system %q: %s", + d.Id(), err.Error()) + } + } + if d.HasChange("tags") { err := setTagsEFS(conn, d) if err != nil { @@ -297,6 +341,17 @@ func resourceAwsEfsFileSystemRead(d *schema.ResourceData, meta interface{}) erro return fmt.Errorf("error setting dns_name: %s", err) } + res, err := conn.DescribeLifecycleConfiguration(&efs.DescribeLifecycleConfigurationInput{ + FileSystemId: fs.FileSystemId, + }) + if err != nil { + return fmt.Errorf("Error describing lifecycle configuration for EFS file system (%s): %s", + aws.StringValue(fs.FileSystemId), err) + } + if err := resourceAwsEfsFileSystemSetLifecyclePolicy(d, res.LifecyclePolicies); err != nil { + return err + } + return nil } @@ -379,3 +434,37 @@ func resourceEfsFileSystemCreateUpdateRefreshFunc(id string, conn *efs.EFS) reso return fs, state, nil } } + +func resourceAwsEfsFileSystemSetLifecyclePolicy(d *schema.ResourceData, lp []*efs.LifecyclePolicy) error { + log.Printf("[DEBUG] lifecycle pols: %s %d", lp, len(lp)) + if len(lp) == 0 { + d.Set("lifecycle_policy", nil) + return nil + } + 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 != nil { + data["transition_to_ia"] = *config.TransitionToIA + } + log.Printf("[DEBUG] lp: %s", data) + } + + if err := d.Set("lifecycle_policy", newLP); err != nil { + return fmt.Errorf("error setting lifecycle_policy: %s", err) + } + return nil +} + +func resourceAwsEfsFileSystemLifecyclePolicy(lcPol []interface{}) []*efs.LifecyclePolicy { + result := make([]*efs.LifecyclePolicy, len(lcPol)) + + for i := 0; i < len(lcPol); i++ { + lp := lcPol[i].(map[string]interface{}) + result[i] = &efs.LifecyclePolicy{TransitionToIA: aws.String(lp["transition_to_ia"].(string))} + } + return result +} diff --git a/aws/resource_aws_efs_file_system_test.go b/aws/resource_aws_efs_file_system_test.go index 135c42e61ee..ff193df1744 100644 --- a/aws/resource_aws_efs_file_system_test.go +++ b/aws/resource_aws_efs_file_system_test.go @@ -47,7 +47,6 @@ func TestAccAWSEFSFileSystem_importBasic(t *testing.T) { { Config: testAccAWSEFSFileSystemConfigWithTags(rInt), }, - { ResourceName: resourceName, ImportState: true, @@ -261,6 +260,123 @@ func TestAccAWSEFSFileSystem_ThroughputMode(t *testing.T) { }) } +func TestAccAWSEFSFileSystem_lifecyclePolicy(t *testing.T) { + resourceName := "aws_efs_file_system.foo-with-lifecycle-policy" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckEfsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_ia", + "invalid_value", + ), + ExpectError: regexp.MustCompile(`got invalid_value`), + }, + { + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_ia", + efs.TransitionToIARulesAfter30Days, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckEfsFileSystem(resourceName), + testAccCheckEfsFileSystemLifecyclePolicy(resourceName, "badExpectation"), + ), + ExpectError: regexp.MustCompile(`Expected: badExpectation`), + }, + { + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_ia", + efs.TransitionToIARulesAfter30Days, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckEfsFileSystem(resourceName), + testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter30Days), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEFSFileSystem_lifecyclePolicy_update(t *testing.T) { + resourceName := "aws_efs_file_system.foo-with-lifecycle-policy" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckEfsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_ia", + efs.TransitionToIARulesAfter30Days, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckEfsFileSystem(resourceName), + testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter30Days), + ), + }, + { + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_ia", + efs.TransitionToIARulesAfter90Days, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckEfsFileSystem(resourceName), + testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter90Days), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func TestAccAWSEFSFileSystem_lifecyclePolicy_removal(t *testing.T) { + resourceName := "aws_efs_file_system.foo-with-lifecycle-policy" + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckEfsFileSystemDestroy, + Steps: []resource.TestStep{ + { + Config: testAccAWSEFSFileSystemConfigWithLifecyclePolicy( + "transition_to_ia", + efs.TransitionToIARulesAfter14Days, + ), + Check: resource.ComposeTestCheckFunc( + testAccCheckEfsFileSystem(resourceName), + testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter14Days), + ), + }, + { + Config: testAccAWSEFSFileSystemConfigRemovedLifecyclePolicy, + Check: resource.ComposeTestCheckFunc( + testAccCheckEfsFileSystem(resourceName), + testAccCheckEfsFileSystemLifecyclePolicy(resourceName, efs.TransitionToIARulesAfter14Days), + ), + ExpectError: regexp.MustCompile(fmt.Sprintf(`Expected: %s`, efs.TransitionToIARulesAfter14Days)), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + func testAccCheckEfsFileSystemDestroy(s *terraform.State) error { conn := testAccProvider.Meta().(*AWSClient).efsconn for _, rs := range s.RootModule().Resources { @@ -391,6 +507,50 @@ 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) + } +} + const testAccAWSEFSFileSystemConfig = ` resource "aws_efs_file_system" "foo" { creation_token = "radeksimko" @@ -477,3 +637,17 @@ resource "aws_efs_file_system" "test" { } `, provisionedThroughputInMibps) } + +func testAccAWSEFSFileSystemConfigWithLifecyclePolicy(lpName string, lpVal string) string { + return fmt.Sprintf(` +resource "aws_efs_file_system" "foo-with-lifecycle-policy" { + lifecycle_policy { + %s = %q + } +} +`, lpName, lpVal) +} + +const testAccAWSEFSFileSystemConfigRemovedLifecyclePolicy = ` +resource "aws_efs_file_system" "foo-with-lifecycle-policy" {} +` diff --git a/website/docs/r/efs_file_system.html.markdown b/website/docs/r/efs_file_system.html.markdown index cfb9b8274bb..de50f7983f9 100644 --- a/website/docs/r/efs_file_system.html.markdown +++ b/website/docs/r/efs_file_system.html.markdown @@ -12,6 +12,8 @@ Provides an Elastic File System (EFS) resource. ## Example Usage +### EFS File System w/ tags + ```hcl resource "aws_efs_file_system" "foo" { creation_token = "my-product" @@ -22,6 +24,18 @@ resource "aws_efs_file_system" "foo" { } ``` +### Using lifecycle policy + +```hcl +resource "aws_efs_file_system" "foo_with_lifecyle_policy" { + creation_token = "my-product" + + lifecycle_policy { + transition_to_ia = "AFTER_30_DAYS" + } +} +``` + ## Argument Reference The following arguments are supported: @@ -32,11 +46,17 @@ system creation. By default generated by Terraform. See [Elastic File System] (http://docs.aws.amazon.com/efs/latest/ug/) user guide for more information. * `encrypted` - (Optional) If true, the disk will be encrypted. * `kms_key_id` - (Optional) The ARN for the KMS encryption key. When specifying kms_key_id, encrypted needs to be set to true. +* `lifecycle_policy` - (Optional) A file system [lifecycle policy](https://docs.aws.amazon.com/efs/latest/ug/API_LifecyclePolicy.html) object (documented below). * `performance_mode` - (Optional) The file system performance mode. Can be either `"generalPurpose"` or `"maxIO"` (Default: `"generalPurpose"`). * `provisioned_throughput_in_mibps` - (Optional) The throughput, measured in MiB/s, that you want to provision for the file system. Only applicable with `throughput_mode` set to `provisioned`. * `tags` - (Optional) A mapping of tags to assign to the file system. * `throughput_mode` - (Optional) Throughput mode for the file system. Defaults to `bursting`. Valid values: `bursting`, `provisioned`. When using `provisioned`, also set `provisioned_throughput_in_mibps`. +### Lifecycle Policy Arguments +For **lifecycle_policy** the following attributes are supported: + +* `transition_to_ia` - (Optional) Indicates how long it takes to transition files to the IA storage class. Valid values: `AFTER_14_DAYS`, `AFTER_30_DAYS`, `AFTER_60_DAYS`, or `AFTER_90_DAYS`. + ## Attributes Reference In addition to all arguments above, the following attributes are exported: