Skip to content

Commit

Permalink
Merge pull request #27741 from DrFaust92/redshift-server-snap
Browse files Browse the repository at this point in the history
redshift serverless snapshot resource
  • Loading branch information
ewbankkit authored Nov 10, 2022
2 parents 3444f92 + f0d1208 commit 70d3a77
Show file tree
Hide file tree
Showing 9 changed files with 513 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/27741.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-note:new-resource
aws_redshiftserverless_snapshot
```
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -1915,6 +1915,7 @@ func New(_ context.Context) (*schema.Provider, error) {

"aws_redshiftserverless_endpoint_access": redshiftserverless.ResourceEndpointAccess(),
"aws_redshiftserverless_namespace": redshiftserverless.ResourceNamespace(),
"aws_redshiftserverless_snapshot": redshiftserverless.ResourceSnapshot(),
"aws_redshiftserverless_usage_limit": redshiftserverless.ResourceUsageLimit(),
"aws_redshiftserverless_workgroup": redshiftserverless.ResourceWorkgroup(),

Expand Down
25 changes: 25 additions & 0 deletions internal/service/redshiftserverless/find.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,28 @@ func FindUsageLimitByName(conn *redshiftserverless.RedshiftServerless, id string

return output.UsageLimit, nil
}

func FindSnapshotByName(conn *redshiftserverless.RedshiftServerless, name string) (*redshiftserverless.Snapshot, error) {
input := &redshiftserverless.GetSnapshotInput{
SnapshotName: aws.String(name),
}

output, err := conn.GetSnapshot(input)

if tfawserr.ErrMessageContains(err, redshiftserverless.ErrCodeResourceNotFoundException, "snapshot") {
return nil, &resource.NotFoundError{
LastError: err,
LastRequest: input,
}
}

if err != nil {
return nil, err
}

if output == nil {
return nil, tfresource.NewEmptyResultError(input)
}

return output.Snapshot, nil
}
173 changes: 173 additions & 0 deletions internal/service/redshiftserverless/snapshot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package redshiftserverless

import (
"fmt"
"log"

"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/redshiftserverless"
"github.com/hashicorp/aws-sdk-go-base/v2/awsv1shim/v2/tfawserr"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
"github.com/hashicorp/terraform-provider-aws/internal/flex"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

func ResourceSnapshot() *schema.Resource {
return &schema.Resource{
Create: resourceSnapshotCreate,
Read: resourceSnapshotRead,
Update: resourceSnapshotUpdate,
Delete: resourceSnapshotDelete,

Importer: &schema.ResourceImporter{
State: schema.ImportStatePassthrough,
},

Schema: map[string]*schema.Schema{
"accounts_with_provisioned_restore_access": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"accounts_with_restore_access": {
Type: schema.TypeSet,
Computed: true,
Elem: &schema.Schema{
Type: schema.TypeString,
},
},
"admin_username": {
Type: schema.TypeString,
Computed: true,
},
"arn": {
Type: schema.TypeString,
Computed: true,
},
"kms_key_id": {
Type: schema.TypeString,
Computed: true,
},
"namespace_arn": {
Type: schema.TypeString,
Computed: true,
},
"namespace_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"owner_account": {
Type: schema.TypeString,
Computed: true,
},
"retention_period": {
Type: schema.TypeInt,
Optional: true,
Default: -1,
},
"snapshot_name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
},
}
}

func resourceSnapshotCreate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).RedshiftServerlessConn

input := redshiftserverless.CreateSnapshotInput{
NamespaceName: aws.String(d.Get("namespace_name").(string)),
SnapshotName: aws.String(d.Get("snapshot_name").(string)),
}

if v, ok := d.GetOk("retention_period"); ok {
input.RetentionPeriod = aws.Int64(int64(v.(int)))
}

out, err := conn.CreateSnapshot(&input)

if err != nil {
return fmt.Errorf("error creating Redshift Serverless Snapshot : %w", err)
}

d.SetId(aws.StringValue(out.Snapshot.SnapshotName))

if _, err := waitSnapshotAvailable(conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for Redshift Serverless Snapshot (%s) to be Available: %w", d.Id(), err)
}

return resourceSnapshotRead(d, meta)
}

func resourceSnapshotRead(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).RedshiftServerlessConn

out, err := FindSnapshotByName(conn, d.Id())
if !d.IsNewResource() && tfresource.NotFound(err) {
log.Printf("[WARN] Redshift Serverless Snapshot (%s) not found, removing from state", d.Id())
d.SetId("")
return nil
}

if err != nil {
return fmt.Errorf("error reading Redshift Serverless Snapshot (%s): %w", d.Id(), err)
}

d.Set("arn", out.SnapshotArn)
d.Set("snapshot_name", out.SnapshotName)
d.Set("namespace_name", out.NamespaceName)
d.Set("namespace_arn", out.NamespaceArn)
d.Set("retention_period", out.SnapshotRetentionPeriod)
d.Set("admin_username", out.AdminUsername)
d.Set("kms_key_id", out.KmsKeyId)
d.Set("owner_account", out.OwnerAccount)
d.Set("accounts_with_provisioned_restore_access", flex.FlattenStringSet(out.AccountsWithRestoreAccess))
d.Set("accounts_with_restore_access", flex.FlattenStringSet(out.AccountsWithRestoreAccess))

return nil
}

func resourceSnapshotUpdate(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).RedshiftServerlessConn

input := &redshiftserverless.UpdateSnapshotInput{
SnapshotName: aws.String(d.Id()),
RetentionPeriod: aws.Int64(int64(d.Get("retention_period").(int))),
}

_, err := conn.UpdateSnapshot(input)
if err != nil {
return fmt.Errorf("error updating Redshift Serverless Snapshot (%s): %w", d.Id(), err)
}

return resourceSnapshotRead(d, meta)
}

func resourceSnapshotDelete(d *schema.ResourceData, meta interface{}) error {
conn := meta.(*conns.AWSClient).RedshiftServerlessConn

log.Printf("[DEBUG] Deleting Redshift Serverless Snapshot: %s", d.Id())
_, err := conn.DeleteSnapshot(&redshiftserverless.DeleteSnapshotInput{
SnapshotName: aws.String(d.Id()),
})

if tfawserr.ErrCodeEquals(err, redshiftserverless.ErrCodeResourceNotFoundException) {
return nil
}

if err != nil {
return fmt.Errorf("error deleting Redshift Serverless Snapshot (%s): %w", d.Id(), err)
}

if _, err := waitSnapshotDeleted(conn, d.Id()); err != nil {
return fmt.Errorf("error waiting for Redshift Serverless Snapshot (%s) to be Deleted: %w", d.Id(), err)
}

return nil
}
158 changes: 158 additions & 0 deletions internal/service/redshiftserverless/snapshot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package redshiftserverless_test

import (
"fmt"
"testing"

"github.com/aws/aws-sdk-go/service/redshiftserverless"
sdkacctest "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/hashicorp/terraform-provider-aws/internal/acctest"
"github.com/hashicorp/terraform-provider-aws/internal/conns"
tfredshiftserverless "github.com/hashicorp/terraform-provider-aws/internal/service/redshiftserverless"
"github.com/hashicorp/terraform-provider-aws/internal/tfresource"
)

func TestAccRedshiftServerlessSnapshot_basic(t *testing.T) {
resourceName := "aws_redshiftserverless_snapshot.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, redshiftserverless.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckSnapshotDestroy,
Steps: []resource.TestStep{
{
Config: testAccSnapshotConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckSnapshotExists(resourceName),
resource.TestCheckResourceAttrPair(resourceName, "namespace_name", "aws_redshiftserverless_namespace.test", "id"),
resource.TestCheckResourceAttr(resourceName, "snapshot_name", rName),
resource.TestCheckResourceAttr(resourceName, "retention_period", "-1"),
resource.TestCheckResourceAttr(resourceName, "admin_username", "admin"),
acctest.CheckResourceAttrAccountID(resourceName, "owner_account"),
resource.TestCheckResourceAttr(resourceName, "accounts_with_provisioned_restore_access.#", "0"),
resource.TestCheckResourceAttr(resourceName, "accounts_with_restore_access.#", "0"),
),
},
{
ResourceName: resourceName,
ImportState: true,
ImportStateVerify: true,
},
{
Config: testAccSnapshotConfig_retention(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckSnapshotExists(resourceName),
resource.TestCheckResourceAttrPair(resourceName, "namespace_name", "aws_redshiftserverless_namespace.test", "id"),
resource.TestCheckResourceAttr(resourceName, "snapshot_name", rName),
resource.TestCheckResourceAttr(resourceName, "retention_period", "10"),
acctest.CheckResourceAttrAccountID(resourceName, "owner_account"),
),
},
},
})
}

func TestAccRedshiftServerlessSnapshot_disappears(t *testing.T) {
resourceName := "aws_redshiftserverless_snapshot.test"
rName := sdkacctest.RandomWithPrefix(acctest.ResourcePrefix)

resource.ParallelTest(t, resource.TestCase{
PreCheck: func() { acctest.PreCheck(t) },
ErrorCheck: acctest.ErrorCheck(t, redshiftserverless.EndpointsID),
ProtoV5ProviderFactories: acctest.ProtoV5ProviderFactories,
CheckDestroy: testAccCheckSnapshotDestroy,
Steps: []resource.TestStep{
{
Config: testAccSnapshotConfig_basic(rName),
Check: resource.ComposeTestCheckFunc(
testAccCheckSnapshotExists(resourceName),
acctest.CheckResourceDisappears(acctest.Provider, tfredshiftserverless.ResourceSnapshot(), resourceName),
),
ExpectNonEmptyPlan: true,
},
},
})
}

func testAccCheckSnapshotDestroy(s *terraform.State) error {
conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftServerlessConn

for _, rs := range s.RootModule().Resources {
if rs.Type != "aws_redshiftserverless_snapshot" {
continue
}
_, err := tfredshiftserverless.FindSnapshotByName(conn, rs.Primary.ID)

if tfresource.NotFound(err) {
continue
}

if err != nil {
return err
}

return fmt.Errorf("Redshift Serverless Snapshot %s still exists", rs.Primary.ID)
}

return nil
}

func testAccCheckSnapshotExists(name string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[name]
if !ok {
return fmt.Errorf("not found: %s", name)
}

if rs.Primary.ID == "" {
return fmt.Errorf("Redshift Serverless Snapshot is not set")
}

conn := acctest.Provider.Meta().(*conns.AWSClient).RedshiftServerlessConn

_, err := tfredshiftserverless.FindSnapshotByName(conn, rs.Primary.ID)

return err
}
}

func testAccSnapshotConfig_basic(rName string) string {
return fmt.Sprintf(`
resource "aws_redshiftserverless_namespace" "test" {
namespace_name = %[1]q
}
resource "aws_redshiftserverless_workgroup" "test" {
namespace_name = aws_redshiftserverless_namespace.test.namespace_name
workgroup_name = %[1]q
}
resource "aws_redshiftserverless_snapshot" "test" {
namespace_name = aws_redshiftserverless_workgroup.test.namespace_name
snapshot_name = %[1]q
}
`, rName)
}

func testAccSnapshotConfig_retention(rName string) string {
return fmt.Sprintf(`
resource "aws_redshiftserverless_namespace" "test" {
namespace_name = %[1]q
}
resource "aws_redshiftserverless_workgroup" "test" {
namespace_name = aws_redshiftserverless_namespace.test.namespace_name
workgroup_name = %[1]q
}
resource "aws_redshiftserverless_snapshot" "test" {
namespace_name = aws_redshiftserverless_workgroup.test.namespace_name
snapshot_name = %[1]q
retention_period = 10
}
`, rName)
}
16 changes: 16 additions & 0 deletions internal/service/redshiftserverless/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,19 @@ func statusEndpointAccess(conn *redshiftserverless.RedshiftServerless, name stri
return output, aws.StringValue(output.EndpointStatus), nil
}
}

func statusSnapshot(conn *redshiftserverless.RedshiftServerless, name string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
output, err := FindSnapshotByName(conn, name)

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

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

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

0 comments on commit 70d3a77

Please sign in to comment.