diff --git a/docs/resources/dws_snapshot_copy.md b/docs/resources/dws_snapshot_copy.md new file mode 100644 index 0000000000..e8842f6e17 --- /dev/null +++ b/docs/resources/dws_snapshot_copy.md @@ -0,0 +1,63 @@ +--- +subcategory: "GaussDB(DWS)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_dws_snapshot_copy" +description: |- + Use this resource to copy an automated snapshot to a manual snapshot within HuaweiCloud. +--- + +# huaweicloud_dws_snapshot_copy + +Use this resource to copy an automated snapshot to a manual snapshot within HuaweiCloud. + +-> 1. An automated snapshot can only correspond to one `huaweicloud_dws_snapshot_copy` resource. +
2. Deleting this resource will delete the corresponding copied snapshot. + +## Example Usage + +```hcl +variable "automated_snapshot_id" {} +variable "snapshot_name" {} +variable "description" {} + +resource "huaweicloud_dws_snapshot_copy" "test" { + snapshot_id = var.automated_snapshot_id + name = var.snapshot_name + description = var.description +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String, ForceNew) Specifies the region in which to create the resource. + If omitted, the provider-level region will be used. + Changing this creates a new resource. + +* `snapshot_id` - (Required, String, ForceNew) Specifies the ID of the automated snapshot to be copied. + Changing this creates a new resource. + +* `name` - (Required, String, ForceNew) Specifies the name of the copy snapshot. + Changing this creates a new resource. + The valid length is limited from `4` to `64`, only English letters, digits, underscores (_) and hyphens (-) are allowed, + and must start with a letter. + The snapshot name must be unique. + +* `description` - (Optional, String, ForceNew) Specifies the description of the copy snapshot. + Changing this creates a new resource. + The maximum length is limited to `256` characters, and special characters (`!<>'=&"`) are not allowed. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID, also the ID of the copied snapshot. + +## Import + +The resource can be imported using the related `snapshot_id` and `id`, separated by a slash, e.g. + +```bash +$ terraform import huaweicloud_dws_snapshot_copy.test / +``` diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 5424c2978e..44429ef140 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1470,6 +1470,7 @@ func Provider() *schema.Provider { "huaweicloud_dws_logical_cluster": dws.ResourceLogicalCluster(), "huaweicloud_dws_om_account_action": dws.ResourceOmAccountAction(), "huaweicloud_dws_public_domain_associate": dws.ResourcePublicDomainAssociate(), + "huaweicloud_dws_snapshot_copy": dws.ResourceSnapshotCopy(), "huaweicloud_dws_snapshot_policy": dws.ResourceDwsSnapshotPolicy(), "huaweicloud_dws_snapshot": dws.ResourceDwsSnapshot(), "huaweicloud_dws_workload_configuration": dws.ResourceWorkLoadConfiguration(), diff --git a/huaweicloud/services/acceptance/acceptance.go b/huaweicloud/services/acceptance/acceptance.go index c3214bfd25..571a1ded66 100644 --- a/huaweicloud/services/acceptance/acceptance.go +++ b/huaweicloud/services/acceptance/acceptance.go @@ -397,10 +397,10 @@ var ( HW_DWS_CLUSTER_ID = os.Getenv("HW_DWS_CLUSTER_ID") HW_DWS_LOGICAL_MODE_CLUSTER_ID = os.Getenv("HW_DWS_LOGICAL_MODE_CLUSTER_ID") HW_DWS_LOGICAL_CLUSTER_NAME = os.Getenv("HW_DWS_LOGICAL_CLUSTER_NAME") - - HW_DWS_SNAPSHOT_POLICY_NAME = os.Getenv("HW_DWS_SNAPSHOT_POLICY_NAME") + HW_DWS_SNAPSHOT_POLICY_NAME = os.Getenv("HW_DWS_SNAPSHOT_POLICY_NAME") // The list of the user names under specified DWS cluster. Using commas (,) to separate multiple names. - HW_DWS_ASSOCIATE_USER_NAMES = os.Getenv("HW_DWS_ASSOCIATE_USER_NAMES") + HW_DWS_ASSOCIATE_USER_NAMES = os.Getenv("HW_DWS_ASSOCIATE_USER_NAMES") + HW_DWS_AUTOMATED_SNAPSHOT_ID = os.Getenv("HW_DWS_AUTOMATED_SNAPSHOT_ID") HW_DCS_ACCOUNT_WHITELIST = os.Getenv("HW_DCS_ACCOUNT_WHITELIST") @@ -2089,6 +2089,13 @@ func TestAccPreCheckDwsClusterUserNames(t *testing.T) { } } +// lintignore:AT003 +func TestAccPreCheckDwsAutomatedSnapshot(t *testing.T) { + if HW_DWS_AUTOMATED_SNAPSHOT_ID == "" { + t.Skip("HW_DWS_AUTOMATED_SNAPSHOT_ID must be set for the acceptance test") + } +} + // lintignore:AT003 func TestAccPreCheckDCSAccountWhitelist(t *testing.T) { if HW_DCS_ACCOUNT_WHITELIST == "" { diff --git a/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_copy_test.go b/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_copy_test.go new file mode 100644 index 0000000000..01dab311a4 --- /dev/null +++ b/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_copy_test.go @@ -0,0 +1,88 @@ +package dws + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/dws" +) + +func getCopiedSnapshotFunc(cfg *config.Config, state *terraform.ResourceState) (interface{}, error) { + region := acceptance.HW_REGION_NAME + client, err := cfg.NewServiceClient("dws", region) + if err != nil { + return nil, fmt.Errorf("error creating DWS client: %s", err) + } + + return dws.GetSnapshotById(client, state.Primary.ID) +} + +func TestAccSnapshotCopy_basic(t *testing.T) { + var ( + obj interface{} + rName = "huaweicloud_dws_snapshot_copy.test" + name = acceptance.RandomAccResourceNameWithDash() + + rc = acceptance.InitResourceCheck( + rName, + &obj, + getCopiedSnapshotFunc, + ) + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckDwsAutomatedSnapshot(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: rc.CheckResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testSnapshotCopy_basic_step1(name), + Check: resource.ComposeTestCheckFunc( + rc.CheckResourceExists(), + resource.TestCheckResourceAttr(rName, "name", name), + resource.TestCheckResourceAttr(rName, "snapshot_id", acceptance.HW_DWS_AUTOMATED_SNAPSHOT_ID), + resource.TestCheckResourceAttr(rName, "description", "Copying a snapshot by terraform script"), + ), + }, + { + ResourceName: rName, + ImportState: true, + ImportStateVerify: true, + ImportStateIdFunc: testAccSnapshotCopyImportStateFunc(rName), + }, + }, + }) +} + +func testAccSnapshotCopyImportStateFunc(rName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[rName] + if !ok { + return "", fmt.Errorf("resource (%s) not found: %s", rName, rs) + } + + if rs.Primary.Attributes["snapshot_id"] == "" { + return "", fmt.Errorf("invalid format specified for import ID, want '/', but got '%s/%s'", + rs.Primary.Attributes["snapshot_id"], rs.Primary.ID) + } + return fmt.Sprintf("%s/%s", rs.Primary.Attributes["snapshot_id"], rs.Primary.ID), nil + } +} + +func testSnapshotCopy_basic_step1(name string) string { + return fmt.Sprintf(` +resource "huaweicloud_dws_snapshot_copy" "test" { + snapshot_id = "%[1]s" + name = "%[2]s" + description = "Copying a snapshot by terraform script" +} +`, acceptance.HW_DWS_AUTOMATED_SNAPSHOT_ID, name) +} diff --git a/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_test.go b/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_test.go index 686131496a..0f36169a04 100644 --- a/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_test.go +++ b/huaweicloud/services/acceptance/dws/resource_huaweicloud_dws_snapshot_test.go @@ -2,50 +2,24 @@ package dws import ( "fmt" - "strings" "testing" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/chnsz/golangsdk" - "github.com/chnsz/golangsdk/openstack/dws/v1/cluster" - "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" - "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/dws" ) func getDwsSnapshotResourceFunc(cfg *config.Config, state *terraform.ResourceState) (interface{}, error) { region := acceptance.HW_REGION_NAME - // getDwsSnapshot: Query the DWS snapshot. - var ( - getDwsSnapshotHttpUrl = "v1.0/{project_id}/snapshots/{snapshot_id}" - getDwsSnapshotProduct = "dws" - ) - getDwsSnapshotClient, err := cfg.NewServiceClient(getDwsSnapshotProduct, region) + getDwsSnapshotClient, err := cfg.NewServiceClient("dws", region) if err != nil { - return nil, fmt.Errorf("error creating DWS Client: %s", err) + return nil, fmt.Errorf("error creating DWS client: %s", err) } - getDwsSnapshotPath := getDwsSnapshotClient.Endpoint + getDwsSnapshotHttpUrl - getDwsSnapshotPath = strings.ReplaceAll(getDwsSnapshotPath, "{project_id}", getDwsSnapshotClient.ProjectID) - getDwsSnapshotPath = strings.ReplaceAll(getDwsSnapshotPath, "{snapshot_id}", state.Primary.ID) - - getDwsSnapshotOpt := golangsdk.RequestOpts{ - KeepResponseBody: true, - MoreHeaders: map[string]string{ - "Content-Type": "application/json;charset=UTF-8", - }, - OkCodes: []int{ - 200, - }, - } - getDwsSnapshotResp, err := getDwsSnapshotClient.Request("GET", getDwsSnapshotPath, &getDwsSnapshotOpt) - if err != nil { - return nil, fmt.Errorf("error retrieving DWS snapshot: %s", err) - } - return utils.FlattenResponse(getDwsSnapshotResp) + return dws.GetSnapshotById(getDwsSnapshotClient, state.Primary.ID) } func TestAccDwsSnapshot_basic(t *testing.T) { @@ -61,7 +35,10 @@ func TestAccDwsSnapshot_basic(t *testing.T) { ) resource.ParallelTest(t, resource.TestCase{ - PreCheck: func() { acceptance.TestAccPreCheck(t) }, + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckDwsClusterId(t) + }, ProviderFactories: acceptance.TestAccProviderFactories, CheckDestroy: rc.CheckResourceDestroy(), Steps: []resource.TestStep{ @@ -72,7 +49,7 @@ func TestAccDwsSnapshot_basic(t *testing.T) { resource.TestCheckResourceAttr(rName, "name", name), resource.TestCheckResourceAttr(rName, "status", "AVAILABLE"), resource.TestCheckResourceAttr(rName, "type", "MANUAL"), - resource.TestCheckResourceAttrPair(rName, "cluster_id", "huaweicloud_dws_cluster.test", "id"), + resource.TestCheckResourceAttr(rName, "cluster_id", acceptance.HW_DWS_CLUSTER_ID), resource.TestCheckResourceAttrSet(rName, "started_at"), resource.TestCheckResourceAttrSet(rName, "finished_at"), resource.TestCheckResourceAttrSet(rName, "size"), @@ -89,11 +66,9 @@ func TestAccDwsSnapshot_basic(t *testing.T) { func testDwsSnapshot_basic(name string) string { return fmt.Sprintf(` -%s - resource "huaweicloud_dws_snapshot" "test" { - name = "%s" - cluster_id = huaweicloud_dws_cluster.test.id + name = "%[1]s" + cluster_id = "%[2]s" } -`, testAccDwsCluster_basic_step1(name, 3, cluster.PublicBindTypeNotUse, "cluster123@!"), name) +`, name, acceptance.HW_DWS_CLUSTER_ID) } diff --git a/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot.go b/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot.go index b2e6d3ccf2..321c76c2cf 100644 --- a/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot.go +++ b/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot.go @@ -7,7 +7,6 @@ package dws import ( "context" - "encoding/json" "fmt" "strings" "time" @@ -106,7 +105,7 @@ func resourceDwsSnapshotCreate(ctx context.Context, d *schema.ResourceData, meta ) createDwsSnapshotClient, err := cfg.NewServiceClient(createDwsSnapshotProduct, region) if err != nil { - return diag.Errorf("error creating DWS Client: %s", err) + return diag.Errorf("error creating DWS client: %s", err) } createDwsSnapshotPath := createDwsSnapshotClient.Endpoint + createDwsSnapshotHttpUrl @@ -164,7 +163,7 @@ func createDwsSnapshotWaitingForStateCompleted(ctx context.Context, d *schema.Re ) createDwsSnapshotWaitingClient, err := cfg.NewServiceClient(createDwsSnapshotWaitingProduct, region) if err != nil { - return nil, "ERROR", fmt.Errorf("error creating DWS Client: %s", err) + return nil, "ERROR", fmt.Errorf("error creating DWS client: %s", err) } createDwsSnapshotWaitingPath := createDwsSnapshotWaitingClient.Endpoint + createDwsSnapshotWaitingHttpUrl @@ -217,43 +216,40 @@ func createDwsSnapshotWaitingForStateCompleted(ctx context.Context, d *schema.Re return err } -func resourceDwsSnapshotRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { - cfg := meta.(*config.Config) - region := cfg.GetRegion(d) - - var mErr *multierror.Error - - // getDwsSnapshot: Query the DWS snapshot. - var ( - getDwsSnapshotHttpUrl = "v1.0/{project_id}/snapshots/{snapshot_id}" - getDwsSnapshotProduct = "dws" - ) - getDwsSnapshotClient, err := cfg.NewServiceClient(getDwsSnapshotProduct, region) - if err != nil { - return diag.Errorf("error creating DWS Client: %s", err) - } +// GetSnapshotById is a method used to query snapshot detail. +func GetSnapshotById(client *golangsdk.ServiceClient, snapshotId string) (interface{}, error) { + httpUrl := "v1.0/{project_id}/snapshots/{snapshot_id}" + getPath := client.Endpoint + httpUrl + getPath = strings.ReplaceAll(getPath, "{project_id}", client.ProjectID) + getPath = strings.ReplaceAll(getPath, "{snapshot_id}", snapshotId) - getDwsSnapshotPath := getDwsSnapshotClient.Endpoint + getDwsSnapshotHttpUrl - getDwsSnapshotPath = strings.ReplaceAll(getDwsSnapshotPath, "{project_id}", getDwsSnapshotClient.ProjectID) - getDwsSnapshotPath = strings.ReplaceAll(getDwsSnapshotPath, "{snapshot_id}", d.Id()) - - getDwsSnapshotOpt := golangsdk.RequestOpts{ + opt := golangsdk.RequestOpts{ KeepResponseBody: true, MoreHeaders: requestOpts.MoreHeaders, } - getDwsSnapshotResp, err := getDwsSnapshotClient.Request("GET", getDwsSnapshotPath, &getDwsSnapshotOpt) + resp, err := client.Request("GET", getPath, &opt) + if err != nil { + // "DWS.5149": The copied snapshot does not exist. + return nil, common.ConvertExpected400ErrInto404Err(err, "error_code", "DWS.5149") + } + + return utils.FlattenResponse(resp) +} +func resourceDwsSnapshotRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + getDwsSnapshotClient, err := cfg.NewServiceClient("dws", region) if err != nil { - return common.CheckDeletedDiag(d, parseSnapshotNotFoundError(err), "error retrieving DWS snapshot") + return diag.Errorf("error creating DWS client: %s", err) } - getDwsSnapshotRespBody, err := utils.FlattenResponse(getDwsSnapshotResp) + getDwsSnapshotRespBody, err := GetSnapshotById(getDwsSnapshotClient, d.Id()) if err != nil { - return diag.FromErr(err) + return common.CheckDeletedDiag(d, err, "error retrieving DWS snapshot") } - mErr = multierror.Append( - mErr, + mErr := multierror.Append( d.Set("region", region), d.Set("name", utils.PathSearch("snapshot.name", getDwsSnapshotRespBody, nil)), d.Set("cluster_id", utils.PathSearch("snapshot.cluster_id", getDwsSnapshotRespBody, nil)), @@ -268,50 +264,35 @@ func resourceDwsSnapshotRead(_ context.Context, d *schema.ResourceData, meta int return diag.FromErr(mErr.ErrorOrNil()) } -func parseSnapshotNotFoundError(respErr error) error { - var apiErr interface{} - if errCode, ok := respErr.(golangsdk.ErrDefault400); ok && errCode.Body != nil { - pErr := json.Unmarshal(errCode.Body, &apiErr) - if pErr != nil { - return pErr - } - errCode, err := jmespath.Search(`error_code`, apiErr) - if err != nil { - return fmt.Errorf("error parse errorCode from response body: %s", err.Error()) - } - - if errCode == `DWS.5149` { - return golangsdk.ErrDefault404{} - } +// deleteSnapshotById is a method used to delete snapshot. +func deleteSnapshotById(client *golangsdk.ServiceClient, snapshotId string) error { + deleteHttpUrl := "v1.0/{project_id}/snapshots/{snapshot_id}" + deletePath := client.Endpoint + deleteHttpUrl + deletePath = strings.ReplaceAll(deletePath, "{project_id}", client.ProjectID) + deletePath = strings.ReplaceAll(deletePath, "{snapshot_id}", snapshotId) + + deleteOpt := golangsdk.RequestOpts{ + MoreHeaders: requestOpts.MoreHeaders, + KeepResponseBody: true, + } + _, err := client.Request("DELETE", deletePath, &deleteOpt) + if err != nil { + // "DWS.0001": The snapshot has been deleted. + return common.ConvertExpected400ErrInto404Err(err, "error_code", "DWS.0001") } - return respErr -} + return nil +} func resourceDwsSnapshotDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { cfg := meta.(*config.Config) region := cfg.GetRegion(d) - - // deleteDwsSnapshot: delete DWS snapshot - var ( - deleteDwsSnapshotHttpUrl = "v1.0/{project_id}/snapshots/{snapshot_id}" - deleteDwsSnapshotProduct = "dws" - ) - deleteDwsSnapshotClient, err := cfg.NewServiceClient(deleteDwsSnapshotProduct, region) + deleteDwsSnapshotClient, err := cfg.NewServiceClient("dws", region) if err != nil { - return diag.Errorf("error creating DWS Client: %s", err) + return diag.Errorf("error creating DWS client: %s", err) } - deleteDwsSnapshotPath := deleteDwsSnapshotClient.Endpoint + deleteDwsSnapshotHttpUrl - deleteDwsSnapshotPath = strings.ReplaceAll(deleteDwsSnapshotPath, "{project_id}", deleteDwsSnapshotClient.ProjectID) - deleteDwsSnapshotPath = strings.ReplaceAll(deleteDwsSnapshotPath, "{snapshot_id}", d.Id()) - - deleteDwsSnapshotOpt := golangsdk.RequestOpts{ - MoreHeaders: requestOpts.MoreHeaders, - KeepResponseBody: true, - } - _, err = deleteDwsSnapshotClient.Request("DELETE", deleteDwsSnapshotPath, &deleteDwsSnapshotOpt) - if err != nil { - return diag.Errorf("error deleting DWS snapshot: %s", err) + if err = deleteSnapshotById(deleteDwsSnapshotClient, d.Id()); err != nil { + return common.CheckDeletedDiag(d, err, "error deleting DWS snapshot") } return nil diff --git a/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot_copy.go b/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot_copy.go new file mode 100644 index 0000000000..16fdbeb558 --- /dev/null +++ b/huaweicloud/services/dws/resource_huaweicloud_dws_snapshot_copy.go @@ -0,0 +1,158 @@ +package dws + +import ( + "context" + "fmt" + "strings" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + + "github.com/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/common" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +// @API DWS POST /v1.0/{project_id}/snapshots/{snapshot_id}/linked-copy +// @API DWS GET /v1.0/{project_id}/snapshots/{snapshot_id} +// @API DWS DELETE /v1.0/{project_id}/snapshots/{snapshot_id} +func ResourceSnapshotCopy() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceSnapshotCopyCreate, + ReadContext: resourceSnapshotCopyRead, + DeleteContext: resourceSnapshotCopyDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceSnapshotCopyImportState, + }, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "snapshot_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Specifies the ID of the automated snapshot to be copied.", + }, + "name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Specifies the name of the copy snapshot.`, + }, + "description": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Specifies the description of the copy snapshot.`, + }, + }, + } +} + +func resourceSnapshotCopyCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + snapshotId := d.Get("snapshot_id").(string) + // For the same automatic snapshot, you cannot perform the copy snapshot and restore cluster operations at the same time. + config.MutexKV.Lock(snapshotId) + config.MutexKV.Unlock(snapshotId) + + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("dws", region) + if err != nil { + return diag.Errorf("error creating DWS client: %s", err) + } + + httpUrl := "v1.0/{project_id}/snapshots/{snapshot_id}/linked-copy" + createPath := client.Endpoint + httpUrl + createPath = strings.ReplaceAll(createPath, "{project_id}", client.ProjectID) + createPath = strings.ReplaceAll(createPath, "{snapshot_id}", d.Get("snapshot_id").(string)) + + opt := golangsdk.RequestOpts{ + MoreHeaders: requestOpts.MoreHeaders, + KeepResponseBody: true, + JSONBody: utils.RemoveNil(buildSnapshotCopyBodyParams(d)), + } + + resp, err := client.Request("POST", createPath, &opt) + if err != nil { + return diag.Errorf("error creating DWS snapshot copy: %s", err) + } + + respBody, err := utils.FlattenResponse(resp) + if err != nil { + return diag.FromErr(err) + } + + id := utils.PathSearch("snapshot_id", respBody, nil) + if err != nil { + return diag.Errorf("error copying DWS automated snapshot: ID is not found in API response") + } + + d.SetId(id.(string)) + + return resourceSnapshotCopyRead(ctx, d, meta) +} + +func buildSnapshotCopyBodyParams(d *schema.ResourceData) map[string]interface{} { + bodyParams := map[string]interface{}{ + "backup_name": d.Get("name"), + "description": utils.ValueIgnoreEmpty(d.Get("description")), + } + return bodyParams +} + +func resourceSnapshotCopyRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("dws", region) + if err != nil { + return diag.Errorf("error creating DWS client: %s", err) + } + + respBody, err := GetSnapshotById(client, d.Id()) + if err != nil { + return common.CheckDeletedDiag(d, err, "error retrieving copied snapshot") + } + mErr := multierror.Append( + d.Set("region", region), + d.Set("name", utils.PathSearch("snapshot.name", respBody, nil)), + d.Set("description", utils.PathSearch("snapshot.description", respBody, nil)), + ) + + return diag.FromErr(mErr.ErrorOrNil()) +} + +func resourceSnapshotCopyDelete(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("dws", region) + if err != nil { + return diag.Errorf("error creating DWS client: %s", err) + } + + if err = deleteSnapshotById(client, d.Id()); err != nil { + return common.CheckDeletedDiag(d, err, "error deleting copied snapshot") + } + return nil +} + +func resourceSnapshotCopyImportState(_ context.Context, d *schema.ResourceData, _ interface{}) ([]*schema.ResourceData, error) { + importedId := d.Id() + parts := strings.Split(importedId, "/") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid format specified for import ID, want '/', but got '%s'", + importedId) + } + + mErr := multierror.Append(nil, d.Set("snapshot_id", parts[0])) + d.SetId(parts[1]) + return []*schema.ResourceData{d}, mErr.ErrorOrNil() +}