diff --git a/docs/resources/cph_phone_data_restore.md b/docs/resources/cph_phone_data_restore.md new file mode 100644 index 0000000000..c68a09d83b --- /dev/null +++ b/docs/resources/cph_phone_data_restore.md @@ -0,0 +1,53 @@ +--- +subcategory: "Cloud Phone (CPH)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_cph_phone_data_restore" +description: |- + Manages a CPH phone data restore resource within HuaweiCloud. +--- + +# huaweicloud_cph_phone_data_restore + +Manages a CPH phone data restore resource within HuaweiCloud. + +-> The current resource is a one-time resource, and destroying this resource is only removed from the state. + +## Example Usage + +```hcl +variable "phone_id" {} +variable "bucket_name" {} +variable "object_path" {} + +resource "huaweicloud_cph_phone_data_restore" "test" { + phone_id = var.phone_id + bucket_name = var.bucket_name + object_path = var.object_path +} +``` + +## 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. + +* `phone_id` - (Required, String) Specifies the phone ID. + +* `bucket_name` - (Required, String) Specifies the bucket name of OBS. + +* `object_path` - (Required, String) Specifies the object path of OBS. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID. + +## Timeouts + +This resource provides the following timeouts configuration options: + +* `create` - Default is 30 minutes. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 19946a3077..87f210e212 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1564,14 +1564,15 @@ func Provider() *schema.Provider { "huaweicloud_coc_script": coc.ResourceScript(), "huaweicloud_coc_script_execute": coc.ResourceScriptExecute(), - "huaweicloud_cph_server": cph.ResourceCphServer(), - "huaweicloud_cph_adb_command": cph.ResourceAdbCommand(), - "huaweicloud_cph_phone_stop": cph.ResourcePhoneStop(), - "huaweicloud_cph_server_restart": cph.ResourceServerRestart(), - "huaweicloud_cph_phone_reset": cph.ResourcePhoneReset(), - "huaweicloud_cph_share_app": cph.ResourceShareApp(), - "huaweicloud_cph_phone_property": cph.ResourcePhoneProperty(), - "huaweicloud_cph_phone_restart": cph.ResourcePhoneRestart(), + "huaweicloud_cph_server": cph.ResourceCphServer(), + "huaweicloud_cph_adb_command": cph.ResourceAdbCommand(), + "huaweicloud_cph_phone_stop": cph.ResourcePhoneStop(), + "huaweicloud_cph_server_restart": cph.ResourceServerRestart(), + "huaweicloud_cph_phone_reset": cph.ResourcePhoneReset(), + "huaweicloud_cph_share_app": cph.ResourceShareApp(), + "huaweicloud_cph_phone_property": cph.ResourcePhoneProperty(), + "huaweicloud_cph_phone_restart": cph.ResourcePhoneRestart(), + "huaweicloud_cph_phone_data_restore": cph.ResourcePhoneDataRestore(), "huaweicloud_cse_microservice": cse.ResourceMicroservice(), "huaweicloud_cse_microservice_engine": cse.ResourceMicroserviceEngine(), diff --git a/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_data_restore_test.go b/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_data_restore_test.go new file mode 100644 index 0000000000..854f7d1923 --- /dev/null +++ b/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_data_restore_test.go @@ -0,0 +1,51 @@ +package cph + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccCphPhoneDataRestore_basic(t *testing.T) { + name := acceptance.RandomAccResourceName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPrecheckCphObsBucketName(t) + acceptance.TestAccPrecheckCphAdbObjectPath(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testCphPhoneDataRestore_basic(name), + }, + { + Config: testCphServerBase(name), + Check: resource.ComposeTestCheckFunc( + waitForDeletionCooldownComplete(), + ), + }, + }, + }) +} + +func testCphPhoneDataRestore_basic(name string) string { + return fmt.Sprintf(` +%[1]s + +data "huaweicloud_cph_phones" "test" { + server_id = huaweicloud_cph_server.test.id +} + +resource "huaweicloud_cph_phone_data_restore" "test" { + phone_id = data.huaweicloud_cph_phones.test.phones[0].phone_id + bucket_name = "%[2]s" + object_path = "%[3]s" +} +`, testCphServer_basic(name), acceptance.HW_CPH_OBS_BUCKET_NAME, acceptance.HW_CPH_OBS_OBJECT_PATH) +} diff --git a/huaweicloud/services/cph/resource_huaweicloud_cph_phone_data_restore.go b/huaweicloud/services/cph/resource_huaweicloud_cph_phone_data_restore.go new file mode 100644 index 0000000000..b607c97b6d --- /dev/null +++ b/huaweicloud/services/cph/resource_huaweicloud_cph_phone_data_restore.go @@ -0,0 +1,161 @@ +package cph + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + + "github.com/chnsz/golangsdk" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +var phoneDataRestoreNonUpdatableParams = []string{ + "phone_id", + "bucket_name", + "object_path", +} + +// @API CPH POST /v1/{project_id}/cloud-phone/phones/batch-restore +// @API CPH GET /v1/{project_id}/cloud-phone/phones/{phone_id} +func ResourcePhoneDataRestore() *schema.Resource { + return &schema.Resource{ + CreateContext: resourcePhoneDataRestoreCreate, + UpdateContext: resourcePhoneDataRestoreUpdate, + ReadContext: resourcePhoneDataRestoreRead, + DeleteContext: resourcePhoneDataRestoreDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + }, + + CustomizeDiff: config.FlexibleForceNew(phoneDataRestoreNonUpdatableParams), + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "phone_id": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the phone ID.`, + }, + "bucket_name": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the bucket name of OBS.`, + }, + "object_path": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the object path of OBS.`, + }, + "enable_force_new": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"true", "false"}, false), + Description: utils.SchemaDesc("", utils.SchemaDescInput{Internal: true}), + }, + }, + } +} + +func resourcePhoneDataRestoreCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + + client, err := cfg.NewServiceClient("cph", region) + if err != nil { + return diag.Errorf("error creating CPH client: %s", err) + } + + // createPhoneDataRestore: create CPH phone data restore + createPhoneDataRestoreHttpUrl := "v1/{project_id}/cloud-phone/phones/batch-restore" + createPhoneDataRestorePath := client.Endpoint + createPhoneDataRestoreHttpUrl + createPhoneDataRestorePath = strings.ReplaceAll(createPhoneDataRestorePath, "{project_id}", client.ProjectID) + + createPhoneDataRestoreOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + + createPhoneDataRestoreOpt.JSONBody = map[string]interface{}{ + "restore_infos": []map[string]interface{}{ + { + "phone_id": d.Get("phone_id"), + "bucket_name": d.Get("bucket_name"), + "object_path": d.Get("object_path"), + }, + }, + } + createPhoneDataRestoreResp, err := client.Request("POST", createPhoneDataRestorePath, &createPhoneDataRestoreOpt) + if err != nil { + return diag.Errorf("error creating CPH phone data restore: %s", err) + } + + resp, err := utils.FlattenResponse(createPhoneDataRestoreResp) + if err != nil { + return diag.FromErr(err) + } + id := utils.PathSearch("jobs|[0].phone_id", resp, "").(string) + if id == "" { + return diag.Errorf("unable to find the phone ID from the API response") + } + d.SetId(id) + + errorCode := utils.PathSearch("jobs|[0].error_code", resp, "").(string) + if errorCode != "" { + errorMsg := utils.PathSearch("jobs|[0].error_msg", resp, "").(string) + return diag.Errorf("failed to restore CPH phone (phone_id: %s) data, error_code: %s, error_msg: %s", id, errorCode, errorMsg) + } + + err = checkPhoneDataRestoreJobStatus(ctx, client, d.Id(), d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourcePhoneDataRestoreRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourcePhoneDataRestoreUpdate(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourcePhoneDataRestoreDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + errorMsg := "Deleting CPH phone data restore resource is not supported. The resource is only removed from the state." + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: errorMsg, + }, + } +} + +func checkPhoneDataRestoreJobStatus(ctx context.Context, client *golangsdk.ServiceClient, id string, timeout time.Duration) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{"PENDING"}, + Target: []string{"COMPLETED"}, + Refresh: jobStatusRefreshFunc(client, id), + Timeout: timeout, + PollInterval: 10 * timeout, + Delay: 10 * time.Second, + } + _, err := stateConf.WaitForStateContext(ctx) + if err != nil { + return fmt.Errorf("error waiting for CPH phone data restore to be completed: %s", err) + } + return nil +}