Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add efs_file_system_backup_policy resource #18006

Merged
merged 11 commits into from
Jun 23, 2021
3 changes: 3 additions & 0 deletions .changelog/18006.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_efs_backup_policy
```
36 changes: 36 additions & 0 deletions aws/internal/service/efs/finder/finder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package finder

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"
)

func BackupPolicyByID(conn *efs.EFS, id string) (*efs.BackupPolicy, error) {
input := &efs.DescribeBackupPolicyInput{
FileSystemId: aws.String(id),
}

output, err := conn.DescribeBackupPolicy(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.BackupPolicy == nil {
return nil, &resource.NotFoundError{
Message: "Empty result",
LastRequest: input,
}
}

return output.BackupPolicy, nil
}
18 changes: 18 additions & 0 deletions aws/internal/service/efs/waiter/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/efs"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/service/efs/finder"
"github.com/terraform-providers/terraform-provider-aws/aws/internal/tfresource"
)

// AccessPointLifeCycleState fetches the Access Point and its LifecycleState
Expand All @@ -29,6 +31,22 @@ func AccessPointLifeCycleState(conn *efs.EFS, accessPointId string) resource.Sta
}
}

func BackupPolicyStatus(conn *efs.EFS, id string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := finder.BackupPolicyByID(conn, id)

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

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

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

// FileSystemLifeCycleState fetches the Access Point and its LifecycleState
func FileSystemLifeCycleState(conn *efs.EFS, fileSystemID string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
Expand Down
39 changes: 38 additions & 1 deletion aws/internal/service/efs/waiter/waiter.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ const (
FileSystemDeletedTimeout = 10 * time.Minute
FileSystemDeletedDelayTimeout = 2 * time.Second
FileSystemDeletedMinTimeout = 3 * time.Second

BackupPolicyDisabledTimeout = 10 * time.Minute
BackupPolicyEnabledTimeout = 10 * time.Minute
)

// AccessPointCreated waits for an Operation to return Success
Expand All @@ -37,7 +40,7 @@ func AccessPointCreated(conn *efs.EFS, accessPointId string) (*efs.AccessPointDe
return nil, err
}

// AccessPointDelete waits for an Access Point to return Deleted
// AccessPointDeleted waits for an Access Point to return Deleted
func AccessPointDeleted(conn *efs.EFS, accessPointId string) (*efs.AccessPointDescription, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{efs.LifeCycleStateAvailable, efs.LifeCycleStateDeleting, efs.LifeCycleStateDeleted},
Expand Down Expand Up @@ -94,3 +97,37 @@ func FileSystemDeleted(conn *efs.EFS, fileSystemID string) (*efs.FileSystemDescr

return nil, err
}

func BackupPolicyDisabled(conn *efs.EFS, id string) (*efs.BackupPolicy, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{efs.StatusDisabling},
Target: []string{efs.StatusDisabled},
Refresh: BackupPolicyStatus(conn, id),
Timeout: BackupPolicyDisabledTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.(*efs.BackupPolicy); ok {
return output, err
}

return nil, err
}

func BackupPolicyEnabled(conn *efs.EFS, id string) (*efs.BackupPolicy, error) {
stateConf := &resource.StateChangeConf{
Pending: []string{efs.StatusEnabling},
Target: []string{efs.StatusEnabled},
Refresh: BackupPolicyStatus(conn, id),
Timeout: BackupPolicyEnabledTimeout,
}

outputRaw, err := stateConf.WaitForState()

if output, ok := outputRaw.(*efs.BackupPolicy); ok {
return output, err
}

return nil, err
}
1 change: 1 addition & 0 deletions aws/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,7 @@ func Provider() *schema.Provider {
"aws_ecs_service": resourceAwsEcsService(),
"aws_ecs_task_definition": resourceAwsEcsTaskDefinition(),
"aws_efs_access_point": resourceAwsEfsAccessPoint(),
"aws_efs_backup_policy": resourceAwsEfsBackupPolicy(),
"aws_efs_file_system": resourceAwsEfsFileSystem(),
"aws_efs_file_system_policy": resourceAwsEfsFileSystemPolicy(),
"aws_efs_mount_target": resourceAwsEfsMountTarget(),
Expand Down
8 changes: 4 additions & 4 deletions aws/resource_aws_backup_vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,13 @@ func resourceAwsBackupVaultUpdate(d *schema.ResourceData, meta interface{}) erro
func resourceAwsBackupVaultDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*AWSClient).backupconn

input := &backup.DeleteBackupVaultInput{
log.Printf("[DEBUG] Deleting Backup Vault: %s", d.Id())
_, err := conn.DeleteBackupVault(&backup.DeleteBackupVaultInput{
BackupVaultName: aws.String(d.Id()),
}
})

_, err := conn.DeleteBackupVault(input)
if err != nil {
return fmt.Errorf("error deleting Backup Vault (%s): %s", d.Id(), err)
return fmt.Errorf("error deleting Backup Vault (%s): %w", d.Id(), err)
}

return nil
Expand Down
65 changes: 44 additions & 21 deletions aws/resource_aws_backup_vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@ package aws
import (
"fmt"
"log"
"strings"
"testing"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/backup"
"github.com/hashicorp/go-multierror"
multierror "github.com/hashicorp/go-multierror"
"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"
Expand All @@ -31,35 +30,59 @@ func testSweepBackupVaults(region string) error {
if err != nil {
return fmt.Errorf("Error getting client: %w", err)
}

conn := client.(*AWSClient).backupconn
sweepResources := make([]*testSweepResource, 0)
var errs *multierror.Error

input := &backup.ListBackupVaultsInput{}
var sweeperErrs *multierror.Error
sweepResources := make([]*testSweepResource, 0)

err = conn.ListBackupVaultsPages(input, func(page *backup.ListBackupVaultsOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, vault := range page.BackupVaultList {
if vault == nil {
continue
failedToDeleteRecoveryPoint := false
name := aws.StringValue(vault.BackupVaultName)
input := &backup.ListRecoveryPointsByBackupVaultInput{
BackupVaultName: aws.String(name),
}

name := aws.StringValue(vault.BackupVaultName)
err := conn.ListRecoveryPointsByBackupVaultPages(input, func(page *backup.ListRecoveryPointsByBackupVaultOutput, lastPage bool) bool {
if page == nil {
return !lastPage
}

for _, recoveryPoint := range page.RecoveryPoints {
arn := aws.StringValue(recoveryPoint.RecoveryPointArn)

log.Printf("[INFO] Deleting Recovery Point (%s) in Backup Vault (%s)", arn, name)
_, err := conn.DeleteRecoveryPoint(&backup.DeleteRecoveryPointInput{
BackupVaultName: aws.String(name),
RecoveryPointArn: aws.String(arn),
})

// Ignore Default Backup Vault in region (cannot be deleted)
// and automated Backups that result in AccessDeniedException when deleted
if name == "Default" || strings.Contains(name, "automatic-backup-vault") {
if err != nil {
log.Printf("[WARN] Failed to delete Recovery Point (%s) in Backup Vault (%s): %s", arn, name, err)
failedToDeleteRecoveryPoint = true
}
}

return !lastPage
})

if err != nil {
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Reovery Points in Backup Vault (%s) for %s: %w", name, region, err))
}

// Ignore Default and Automatic EFS Backup Vaults in region (cannot be deleted)
if name == "Default" || name == "aws/efs/automatic-backup-vault" {
log.Printf("[INFO] Skipping Backup Vault: %s", name)
continue
}

// Backup Vault deletion only supported when empty
// Reference: https://docs.aws.amazon.com/aws-backup/latest/devguide/API_DeleteBackupVault.html
if aws.Int64Value(vault.NumberOfRecoveryPoints) != 0 {
if failedToDeleteRecoveryPoint {
log.Printf("[INFO] Skipping Backup Vault (%s): not empty", name)
continue
}
Expand All @@ -74,20 +97,20 @@ func testSweepBackupVaults(region string) error {
return !lastPage
})

if err != nil {
errs = multierror.Append(errs, fmt.Errorf("error listing Backup Vaults for %s: %w", region, err))
if testSweepSkipSweepError(err) {
log.Printf("[WARN] Skipping Backup Vaults sweep for %s: %s", region, err)
return sweeperErrs.ErrorOrNil()
}

if err := testSweepResourceOrchestrator(sweepResources); err != nil {
errs = multierror.Append(errs, fmt.Errorf("error sweeping Backup Vaults for %s: %w", region, err))
if err != nil {
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error listing Backup Vaults for %s: %w", region, err))
}

if testSweepSkipSweepError(errs.ErrorOrNil()) {
log.Printf("[WARN] Skipping Backup Vaults sweep for %s: %s", region, errs)
return nil
if err := testSweepResourceOrchestrator(sweepResources); err != nil {
sweeperErrs = multierror.Append(sweeperErrs, fmt.Errorf("error sweeping Backup Vaults for %s: %w", region, err))
}

return errs.ErrorOrNil()
return sweeperErrs.ErrorOrNil()
}

func TestAccAwsBackupVault_basic(t *testing.T) {
Expand Down
Loading