Add ability to set Performance Mode in aws_efs_file_system. (#7791)

* Add ability to set Performance Mode in aws_efs_file_system.

The Elastic File System (EFS) allows for setting a Performance Mode during
creation, thus enabling anyone to chose performance of the file system according
to their particular needs. This commit adds an optional "performance_mode"
attribte to the aws_efs_file_system resource so that an appropriate mode can be
set as needed.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>

* Add test coverage for the ValidateFunc used.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>

* Add "creation_token" and deprecate "reference_name".

Add the "creation_token" attribute so that the resource follows the API more
closely (as per the convention), thus deprecate the "reference_name" attribute.

Update tests and documentation accordingly.

Signed-off-by: Krzysztof Wilczynski <krzysztof.wilczynski@linux.com>
This commit is contained in:
Krzysztof Wilczynski 2016-07-28 19:19:39 +09:00 committed by Paul Stack
parent 2195a67748
commit 63a14be8da
5 changed files with 261 additions and 51 deletions

View File

@ -22,7 +22,7 @@ func TestAccAWSEFSFileSystem_importBasic(t *testing.T) {
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"reference_name"},
ImportStateVerifyIgnore: []string{"reference_name", "creation_token"},
},
},
})

View File

@ -24,10 +24,27 @@ func resourceAwsEfsFileSystem() *schema.Resource {
},
Schema: map[string]*schema.Schema{
"creation_token": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validateMaxLength(64),
},
"reference_name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Deprecated: "Please use attribute `creation_token' instead. This attribute might be removed in future releases.",
ConflictsWith: []string{"creation_token"},
ValidateFunc: validateReferenceName,
},
"performance_mode": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
ValidateFunc: validatePerformanceModeType,
},
"tags": tagsSchema(),
@ -38,20 +55,34 @@ func resourceAwsEfsFileSystem() *schema.Resource {
func resourceAwsEfsFileSystemCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).efsconn
referenceName := ""
if v, ok := d.GetOk("reference_name"); ok {
referenceName = v.(string) + "-"
}
token := referenceName + resource.UniqueId()
fs, err := conn.CreateFileSystem(&efs.CreateFileSystemInput{
CreationToken: aws.String(token),
})
if err != nil {
return err
creationToken := ""
if v, ok := d.GetOk("creation_token"); ok {
creationToken = v.(string)
} else {
if v, ok := d.GetOk("reference_name"); ok {
creationToken = resource.PrefixedUniqueId(fmt.Sprintf("%s-", v.(string)))
log.Printf("[WARN] Using deprecated `reference_name' attribute.")
} else {
creationToken = resource.UniqueId()
}
}
createOpts := &efs.CreateFileSystemInput{
CreationToken: aws.String(creationToken),
}
if v, ok := d.GetOk("performance_mode"); ok {
createOpts.PerformanceMode = aws.String(v.(string))
}
log.Printf("[DEBUG] EFS file system create options: %#v", *createOpts)
fs, err := conn.CreateFileSystem(createOpts)
if err != nil {
return fmt.Errorf("Error creating EFS file system: %s", err)
}
log.Printf("[DEBUG] Creating EFS file system: %s", *fs)
d.SetId(*fs.FileSystemId)
log.Printf("[INFO] EFS file system ID: %s", d.Id())
stateConf := &resource.StateChangeConf{
Pending: []string{"creating"},
@ -82,7 +113,7 @@ func resourceAwsEfsFileSystemCreate(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error waiting for EFS file system (%q) to create: %q",
d.Id(), err.Error())
}
log.Printf("[DEBUG] EFS file system created: %q", *fs.FileSystemId)
log.Printf("[DEBUG] EFS file system %q created.", d.Id())
return resourceAwsEfsFileSystemUpdate(d, meta)
}
@ -91,7 +122,8 @@ func resourceAwsEfsFileSystemUpdate(d *schema.ResourceData, meta interface{}) er
conn := meta.(*AWSClient).efsconn
err := setTagsEFS(conn, d)
if err != nil {
return err
return fmt.Errorf("Error setting EC2 tags for EFS file system (%q): %q",
d.Id(), err.Error())
}
return resourceAwsEfsFileSystemRead(d, meta)
@ -119,7 +151,8 @@ func resourceAwsEfsFileSystemRead(d *schema.ResourceData, meta interface{}) erro
FileSystemId: aws.String(d.Id()),
})
if err != nil {
return err
return fmt.Errorf("Error retrieving EC2 tags for EFS file system (%q): %q",
d.Id(), err.Error())
}
d.Set("tags", tagsToMapEFS(tagsResp.Tags))
@ -130,7 +163,7 @@ func resourceAwsEfsFileSystemRead(d *schema.ResourceData, meta interface{}) erro
func resourceAwsEfsFileSystemDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).efsconn
log.Printf("[DEBUG] Deleting EFS file system %s", d.Id())
log.Printf("[DEBUG] Deleting EFS file system: %s", d.Id())
_, err := conn.DeleteFileSystem(&efs.DeleteFileSystemInput{
FileSystemId: aws.String(d.Id()),
})
@ -154,8 +187,7 @@ func resourceAwsEfsFileSystemDelete(d *schema.ResourceData, meta interface{}) er
}
fs := resp.FileSystems[0]
log.Printf("[DEBUG] current status of %q: %q",
*fs.FileSystemId, *fs.LifeCycleState)
log.Printf("[DEBUG] current status of %q: %q", *fs.FileSystemId, *fs.LifeCycleState)
return fs, *fs.LifeCycleState, nil
},
Timeout: 10 * time.Minute,
@ -165,10 +197,31 @@ func resourceAwsEfsFileSystemDelete(d *schema.ResourceData, meta interface{}) er
_, err = stateConf.WaitForState()
if err != nil {
return err
return fmt.Errorf("Error waiting for EFS file system (%q) to delete: %q",
d.Id(), err.Error())
}
log.Printf("[DEBUG] EFS file system %q deleted.", d.Id())
return nil
}
func validateReferenceName(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
creationToken := resource.PrefixedUniqueId(fmt.Sprintf("%s-", value))
if len(creationToken) > 64 {
errors = append(errors, fmt.Errorf(
"%q cannot take the Creation Token over the limit of 64 characters: %q", k, value))
}
return
}
func validatePerformanceModeType(v interface{}, k string) (ws []string, errors []error) {
value := v.(string)
if value != "generalPurpose" && value != "maxIO" {
errors = append(errors, fmt.Errorf(
"%q contains an invalid Performance Mode %q. Valid modes are either %q or %q",
k, value, "generalPurpose", "maxIO"))
}
return
}

View File

@ -9,10 +9,71 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/efs"
"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)
func TestResourceAWSEFSReferenceName_validation(t *testing.T) {
var value string
var errors []error
value = acctest.RandString(128)
_, errors = validateReferenceName(value, "reference_name")
if len(errors) == 0 {
t.Fatalf("Expected to trigger a validation error")
}
value = acctest.RandString(32)
_, errors = validateReferenceName(value, "reference_name")
if len(errors) != 0 {
t.Fatalf("Expected to trigger a validation error")
}
}
func TestResourceAWSEFSPerformanceMode_validation(t *testing.T) {
type testCase struct {
Value string
ErrCount int
}
invalidCases := []testCase{
{
Value: "garrusVakarian",
ErrCount: 1,
},
{
Value: acctest.RandString(80),
ErrCount: 1,
},
}
for _, tc := range invalidCases {
_, errors := validatePerformanceModeType(tc.Value, "performance_mode")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected to trigger a validation error")
}
}
validCases := []testCase{
{
Value: "generalPurpose",
ErrCount: 0,
},
{
Value: "maxIO",
ErrCount: 0,
},
}
for _, tc := range validCases {
_, errors := validatePerformanceModeType(tc.Value, "aws_efs_file_system")
if len(errors) != tc.ErrCount {
t.Fatalf("Expected not to trigger a validation error")
}
}
}
func TestAccAWSEFSFileSystem_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
@ -25,6 +86,10 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
testAccCheckEfsFileSystem(
"aws_efs_file_system.foo",
),
testAccCheckEfsFileSystemPerformanceMode(
"aws_efs_file_system.foo",
"generalPurpose",
),
),
},
resource.TestStep{
@ -33,6 +98,10 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
testAccCheckEfsFileSystem(
"aws_efs_file_system.foo-with-tags",
),
testAccCheckEfsFileSystemPerformanceMode(
"aws_efs_file_system.foo-with-tags",
"generalPurpose",
),
testAccCheckEfsFileSystemTags(
"aws_efs_file_system.foo-with-tags",
map[string]string{
@ -42,6 +111,22 @@ func TestAccAWSEFSFileSystem_basic(t *testing.T) {
),
),
},
resource.TestStep{
Config: testAccAWSEFSFileSystemConfigWithPerformanceMode,
Check: resource.ComposeTestCheckFunc(
testAccCheckEfsFileSystem(
"aws_efs_file_system.foo-with-performance-mode",
),
testAccCheckEfsCreationToken(
"aws_efs_file_system.foo-with-performance-mode",
"supercalifragilisticexpialidocious",
),
testAccCheckEfsFileSystemPerformanceMode(
"aws_efs_file_system.foo-with-performance-mode",
"maxIO",
),
),
},
},
})
}
@ -82,16 +167,41 @@ func testAccCheckEfsFileSystem(resourceID string) resource.TestCheckFunc {
return fmt.Errorf("No ID is set")
}
fs, ok := s.RootModule().Resources[resourceID]
conn := testAccProvider.Meta().(*AWSClient).efsconn
_, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
FileSystemId: aws.String(rs.Primary.ID),
})
if err != nil {
return err
}
return nil
}
}
func testAccCheckEfsCreationToken(resourceID string, expectedToken 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
_, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
FileSystemId: aws.String(fs.Primary.ID),
resp, err := conn.DescribeFileSystems(&efs.DescribeFileSystemsInput{
FileSystemId: aws.String(rs.Primary.ID),
})
fs := resp.FileSystems[0]
if *fs.CreationToken != expectedToken {
return fmt.Errorf("Creation Token mismatch.\nExpected: %s\nGiven: %v",
expectedToken, *fs.CreationToken)
}
if err != nil {
return err
}
@ -111,14 +221,9 @@ func testAccCheckEfsFileSystemTags(resourceID string, expectedTags map[string]st
return fmt.Errorf("No ID is set")
}
fs, ok := s.RootModule().Resources[resourceID]
if !ok {
return fmt.Errorf("Not found: %s", resourceID)
}
conn := testAccProvider.Meta().(*AWSClient).efsconn
resp, err := conn.DescribeTags(&efs.DescribeTagsInput{
FileSystemId: aws.String(fs.Primary.ID),
FileSystemId: aws.String(rs.Primary.ID),
})
if !reflect.DeepEqual(expectedTags, tagsToMapEFS(resp.Tags)) {
@ -134,6 +239,36 @@ func testAccCheckEfsFileSystemTags(resourceID string, expectedTags map[string]st
}
}
func testAccCheckEfsFileSystemPerformanceMode(resourceID string, expectedMode 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),
})
fs := resp.FileSystems[0]
if *fs.PerformanceMode != expectedMode {
return fmt.Errorf("Performance Mode mismatch.\nExpected: %s\nGiven: %v",
expectedMode, *fs.PerformanceMode)
}
if err != nil {
return err
}
return nil
}
}
const testAccAWSEFSFileSystemConfig = `
resource "aws_efs_file_system" "foo" {
reference_name = "radeksimko"
@ -149,3 +284,10 @@ resource "aws_efs_file_system" "foo-with-tags" {
}
}
`
const testAccAWSEFSFileSystemConfigWithPerformanceMode = `
resource "aws_efs_file_system" "foo-with-performance-mode" {
creation_token = "supercalifragilisticexpialidocious"
performance_mode = "maxIO"
}
`

View File

@ -3,12 +3,12 @@ layout: "aws"
page_title: "AWS: aws_efs_file_system"
sidebar_current: "docs-aws-resource-efs-file-system"
description: |-
Provides an EFS file system.
Provides an Elastic File System (EFS) resource.
---
# aws\_efs\_file\_system
Provides an EFS file system.
Provides an Elastic File System (EFS) resource.
## Example Usage
@ -23,21 +23,31 @@ resource "aws_efs_file_system" "foo" {
## Argument Reference
~> **NOTE:** The `reference_name` attribute has been deprecated and might
be removed in future releases, please use `creation_token` instead.
The following arguments are supported:
* `reference_name` - (Optional) A reference name used in Creation Token
* `tags` - (Optional) A mapping of tags to assign to the file system
* `creation_token` - (Optional) A unique name (a maximum of 64 characters are allowed)
used as reference when creating the the Elastic File System to ensure idempotent file
system creation. By default generated by Terraform. See [Elastic File System]
(http://docs.aws.amazon.com/efs/latest/ug/) user guide for more information.
* `reference_name` - **DEPRECATED** (Optional) A reference name used when creating the
`Creation Token` which Amazon EFS uses to ensure idempotent file system creation. By
default generated by Terraform.
* `performance_mode` - (Optional) The file system performance mode. Can be either
`"generalPurpose"` or `"maxIO"` (Default: `"generalPurpose"`).
* `tags` - (Optional) A mapping of tags to assign to the file system.
## Attributes Reference
The following attributes are exported:
* `id` - The ID that identifies the file system
* `id` - The ID that identifies the file system (e.g. fs-ccfc0d65).
## Import
EFS Filesystems can be imported using the `id`, e.g.
The EFS file systems can be imported using the `id`, e.g.
```
$ terraform import aws_efs_file_system.foo fs-6fa144c6

View File

@ -3,13 +3,15 @@ layout: "aws"
page_title: "AWS: aws_efs_mount_target"
sidebar_current: "docs-aws-resource-efs-mount-target"
description: |-
Provides an EFS mount target.
Provides an Elastic File System (EFS) mount target.
---
# aws\_efs\_mount\_target
Provides an EFS mount target. Per [documentation](https://docs.aws.amazon.com/efs/latest/ug/limits.html)
the limit is 1 mount target per AZ for a single EFS file system.
Provides an Elastic File System (EFS) mount target.
~> **NOTE:** As per the current [documentation](https://docs.aws.amazon.com/efs/latest/ug/limits.html)
the limit is 1 mount target per Availability Zone for a single EFS file system.
## Example Usage
@ -35,24 +37,27 @@ resource "aws_subnet" "alpha" {
The following arguments are supported:
* `file_system_id` - (Required) The ID of the file system for which the mount target is intended.
* `subnet_id` - (Required) The ID of the subnet that the mount target is in.
* `ip_address` - (Optional) The address at which the file system may be mounted via the mount target.
* `security_groups` - (Optional) A list of up to 5 VPC security group IDs in effect for the mount target.
* `subnet_id` - (Required) The ID of the subnet to add the mount target in.
* `ip_address` - (Optional) The address (within the address range of the specified subnet) at
which the file system may be mounted via the mount target.
* `security_groups` - (Optional) A list of up to 5 VPC security group IDs (that must
be for the same VPC as subnet specified) in effect for the mount target.
## Attributes Reference
~> **Note:** The `dns_name` attribute is only useful if the mount target is in a VPC with `enable_dns_hostnames = true`.
~> **Note:** The `dns_name` attribute is only useful if the mount target is in a VPC that has
support for DNS hostnames enabled. See [Using DNS with Your VPC](http://docs.aws.amazon.com/AmazonVPC/latest/UserGuide/vpc-dns.html)
and [VPC resource](https://www.terraform.io/docs/providers/aws/r/vpc.html#enable_dns_hostnames) in Terraform for more information.
The following attributes are exported:
* `id` - The ID of the mount target
* `dns_name` - The DNS name for the given subnet/AZ per [documented convention](http://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html)
* `id` - The ID of the mount target.
* `dns_name` - The DNS name for the given subnet/AZ per [documented convention](http://docs.aws.amazon.com/efs/latest/ug/mounting-fs-mount-cmd-dns-name.html).
* `network_interface_id` - The ID of the network interface that Amazon EFS created when it created the mount target.
## Import
EFS Mount Targets can be imported using the `id`, e.g.
The EFS mount targets can be imported using the `id`, e.g.
```
$ terraform import aws_efs_mount_target.alpha fsmt-52a643fb