diff --git a/docs/resources/cph_phone_property.md b/docs/resources/cph_phone_property.md new file mode 100644 index 0000000000..8426a5f2bf --- /dev/null +++ b/docs/resources/cph_phone_property.md @@ -0,0 +1,64 @@ +--- +subcategory: "Cloud Phone (CPH)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_cph_phone_property" +description: |- + Manages a CPH phone property resource within HuaweiCloud. +--- + +# huaweicloud_cph_phone_property + +Manages a CPH phone property resource within HuaweiCloud. + +## Example Usage + +```hcl +variable "phone_id" {} + +resource "huaweicloud_cph_phone_property" "test" { + phones { + phone_id = var.phone_id + property = jsonencode({ + "com.cph.mainkeys":0, + "disable.status.bar":0, + "ro.permission.changed":0, + "ro.horizontal.screen":0, + "ro.install.auto":0, + "ro.com.cph.sfs_enable":0, + "ro.product.manufacturer":"Huawei", + "ro.product.name":"monbox", + "ro.com.cph.notification_disable":0 + }) + } +} +``` + +## 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. + +* `phones` - (Required, List) Specifies the CPH phones. + The [phones](#cph_phones) structure is documented below. + + +The `phones` block supports: + +* `phone_id` - (Required, String) Specifies the phone ID. + +* `property` - (Required, String) Specifies the phone property, the format is json string. + +## 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 dda548f878..5bc6ac7e20 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1435,6 +1435,7 @@ func Provider() *schema.Provider { "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_cse_microservice": cse.ResourceMicroservice(), "huaweicloud_cse_microservice_engine": cse.ResourceMicroserviceEngine(), diff --git a/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_property_test.go b/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_property_test.go new file mode 100644 index 0000000000..985ced69dd --- /dev/null +++ b/huaweicloud/services/acceptance/cph/resource_huaweicloud_cph_phone_property_test.go @@ -0,0 +1,88 @@ +package cph + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccCphPhoneProperty_basic(t *testing.T) { + name := acceptance.RandomAccResourceName() + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + CheckDestroy: nil, + Steps: []resource.TestStep{ + { + Config: testCphPhoneProperty_basic(name), + }, + }, + }) +} + +func testCphPhoneProperty_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_property" "test" { + phones { + phone_id = data.huaweicloud_cph_phones.test.phones[0].phone_id + property = jsonencode({ + "com.cph.mainkeys":0, + "disable.status.bar":0, + "ro.permission.changed":0, + "ro.horizontal.screen":0, + "ro.install.auto":0, + "ro.com.cph.sfs_enable":0, + "ro.product.manufacturer":"Huawei", + "ro.product.name":"monbox", + "ro.com.cph.notification_disable":0 + }) + } +} +`, testCphServer_build(name)) +} + +func testCphServer_build(name string) string { + return fmt.Sprintf(` +%s + +resource "huaweicloud_cph_server" "test" { + name = "%s" + server_flavor = "physical.rx1.xlarge" + phone_flavor = "rx1.cp.c15.d46.e1v1" + image_id = data.huaweicloud_cph_phone_images.test.images[0].id + keypair_name = huaweicloud_kps_keypair.test.name + + vpc_id = huaweicloud_vpc.test.id + subnet_id = huaweicloud_vpc_subnet.test.id + eip_type = "5_bgp" + + bandwidth { + share_type = "0" + charge_mode = "1" + size = 300 + } + + period_unit = "month" + period = 1 + auto_renew = "true" + + lifecycle { + ignore_changes = [ + image_id, auto_renew, period, period_unit, + ] + } +} +`, testCphServerBase(name), name) +} diff --git a/huaweicloud/services/cph/resource_huaweicloud_cph_phone_property.go b/huaweicloud/services/cph/resource_huaweicloud_cph_phone_property.go new file mode 100644 index 0000000000..d48dd3b48e --- /dev/null +++ b/huaweicloud/services/cph/resource_huaweicloud_cph_phone_property.go @@ -0,0 +1,155 @@ +package cph + +import ( + "context" + "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 PhonePropertyNonUpdatableParams = []string{"phones", "phones.*.phone_id", "phones.*.property"} + +// @API CPH POST /v1/{project_id}/cloud-phone/phones/batch-update-property +func ResourcePhoneProperty() *schema.Resource { + return &schema.Resource{ + CreateContext: resourcePhonePropertyCreate, + UpdateContext: resourcePhonePropertyUpdate, + ReadContext: resourcePhonePropertyRead, + DeleteContext: resourcePhonePropertyDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(30 * time.Minute), + }, + + CustomizeDiff: config.FlexibleForceNew(PhoneResetNonUpdatableParams), + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "phones": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: `Specifies the CPH phones.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "phone_id": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the phone ID.`, + }, + "property": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the phone property, the format is json string.`, + }, + }, + }, + }, + "enable_force_new": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice([]string{"true", "false"}, false), + Description: utils.SchemaDesc("", utils.SchemaDescInput{Internal: true}), + }, + }, + } +} + +func resourcePhonePropertyCreate(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) + } + + // createPhoneRestart: create CPH phone reset + updatePhonePropertyHttpUrl := "v1/{project_id}/cloud-phone/phones/batch-update-property" + updatePhonePropertyPath := client.Endpoint + updatePhonePropertyHttpUrl + updatePhonePropertyPath = strings.ReplaceAll(updatePhonePropertyPath, "{project_id}", client.ProjectID) + + updatePhonePropertyOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + } + + updatePhonePropertyOpt.JSONBody = utils.RemoveNil(map[string]interface{}{ + "phones": d.Get("phones"), + }) + updatePhonePropertyResp, err := client.Request("POST", updatePhonePropertyPath, &updatePhonePropertyOpt) + if err != nil { + return diag.Errorf("error updating CPH phone property: %s", err) + } + + resp, err := utils.FlattenResponse(updatePhonePropertyResp) + if err != nil { + return diag.FromErr(err) + } + id := utils.PathSearch("jobs|[0].job_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 != "" { + phoneId := utils.PathSearch("jobs|[0].job_id", resp, "").(string) + errorMsg := utils.PathSearch("jobs|[0].error_msg", resp, "").(string) + return diag.Errorf("failed to updating CPH phone property (phone_id: %s), error_code: %s, error_msg: %s", phoneId, errorCode, errorMsg) + } + + err = checkPhonePropertyJobStatus(ctx, client, d.Id(), d.Timeout(schema.TimeoutCreate)) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +func resourcePhonePropertyRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourcePhonePropertyUpdate(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + return nil +} + +func resourcePhonePropertyDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + errorMsg := "Deleting CPH phone property resource is not supported. The resource is only removed from the state." + return diag.Diagnostics{ + diag.Diagnostic{ + Severity: diag.Warning, + Summary: errorMsg, + }, + } +} + +func checkPhonePropertyJobStatus(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 err + } + return nil +}