diff --git a/docs/resources/cph_phone_data_export.md b/docs/resources/cph_phone_data_export.md new file mode 100644 index 00000000000..f9ea7be4e32 --- /dev/null +++ b/docs/resources/cph_phone_data_export.md @@ -0,0 +1,61 @@ +--- +subcategory: "Cloud Phone (CPH)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_cph_phone_data_export" +description: |- + Manages a CPH phone data export resource within HuaweiCloud. +--- + +# huaweicloud_cph_phone_data_export + +Manages a CPH phone data export 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" {} +variable "include_files" {} +variable "exclude_files" {} + +resource "huaweicloud_cph_phone_data_export" "test" { + phone_id = var.phone_id + bucket_name = var.bucket_name + object_path = var.object_path + include_files = var.include_files + exclude_files = var.exclude_files +} +``` + +## 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. + +* `include_files` - (Required, List) Specifies the include files. + +* `exclude_files` - (Optional, List) Specifies the exclude files. + +## 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 161702e3b99..3fdafdcad08 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1560,14 +1560,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_export": cph.ResourcePhoneDataExport(), "huaweicloud_cse_microservice": cse.ResourceMicroservice(), "huaweicloud_cse_microservice_engine": cse.ResourceMicroserviceEngine(), diff --git a/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_data_export_test.go b/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_data_export_test.go new file mode 100644 index 00000000000..9c5abc13806 --- /dev/null +++ b/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_data_export_test.go @@ -0,0 +1,52 @@ +package cph + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccCphPhoneDataExport_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: testCphPhoneDataExport_basic(name), + }, + { + Config: testCphServerBase(name), + Check: resource.ComposeTestCheckFunc( + waitForDeletionCooldownComplete(), + ), + }, + }, + }) +} + +func testCphPhoneDataExport_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_export" "test" { + phone_id = data.huaweicloud_cph_phones.test.phones[0].phone_id + bucket_name = "%[2]s" + object_path = "%[3]s" + include_files = ["/data/app", "/data/local", "/data/media"] +} +`, 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_export.go b/huaweicloud/services/cph/resource_huaweicloud_cph_phone_data_export.go new file mode 100644 index 00000000000..156c2234731 --- /dev/null +++ b/huaweicloud/services/cph/resource_huaweicloud_cph_phone_data_export.go @@ -0,0 +1,177 @@ +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 phoneDataExportNonUpdatableParams = []string{ + "phone_id", + "bucket_name", + "object_path", + "include_files", + "exclude_files", +} + +// @API CPH POST /v1/{project_id}/cloud-phone/phones/batch-storage +// @API CPH GET /v1/{project_id}/cloud-phone/phones/{phone_id} +func ResourcePhoneDataExport() *schema.Resource { + return &schema.Resource{ + CreateContext: resourcePhoneDataExportCreate, + UpdateContext: resourcePhoneDataExportUpdate, + ReadContext: resourcePhoneDataExportRead, + DeleteContext: resourcePhoneDataExportDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + }, + + CustomizeDiff: config.FlexibleForceNew(phoneDataExportNonUpdatableParams), + + 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.`, + }, + "include_files": { + Type: schema.TypeList, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: `Specifies the include files.`, + }, + "exclude_files": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: `Specifies the exclude files.`, + }, + "enable_force_new": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"true", "false"}, false), + Description: utils.SchemaDesc("", utils.SchemaDescInput{Internal: true}), + }, + }, + } +} + +func resourcePhoneDataExportCreate(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) + } + + // createPhoneDataExport: create CPH phone data export + createPhoneDataExportHttpUrl := "v1/{project_id}/cloud-phone/phones/batch-storage" + createPhoneDataExportPath := client.Endpoint + createPhoneDataExportHttpUrl + createPhoneDataExportPath = strings.ReplaceAll(createPhoneDataExportPath, "{project_id}", client.ProjectID) + + createPhoneDataExportOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + + createPhoneDataExportOpt.JSONBody = utils.RemoveNil(map[string]interface{}{ + "storage_infos": []map[string]interface{}{ + { + "phone_id": d.Get("phone_id"), + "bucket_name": d.Get("bucket_name"), + "object_path": d.Get("object_path"), + "include_files": d.Get("include_files"), + "exclude_files": utils.ValueIgnoreEmpty(d.Get("exclude_files")), + }, + }, + }) + createPhoneDataExportResp, err := client.Request("POST", createPhoneDataExportPath, &createPhoneDataExportOpt) + if err != nil { + return diag.Errorf("error creating CPH phone data export: %s", err) + } + + resp, err := utils.FlattenResponse(createPhoneDataExportResp) + 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 export CPH phone (phone_id: %s) data, error_code: %s, error_msg: %s", id, errorCode, errorMsg) + } + + err = checkPhoneDataExportJobStatus(ctx, client, d.Id(), d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourcePhoneDataExportRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourcePhoneDataExportUpdate(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourcePhoneDataExportDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + errorMsg := "Deleting CPH phone data export resource is not supported. The resource is only removed from the state." + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: errorMsg, + }, + } +} + +func checkPhoneDataExportJobStatus(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 export to be completed: %s", err) + } + return nil +}