From bd9f9c139aecd58e2bd9b4d5bbdc8aa8e7af1100 Mon Sep 17 00:00:00 2001 From: ruwenqiang123 Date: Thu, 19 Dec 2024 19:56:32 +0800 Subject: [PATCH] feat(live): add a new resource to generate a sign URL --- docs/resources/live_url_authentication.md | 80 ++++++++++ huaweicloud/provider.go | 1 + ...uaweicloud_live_url_authentication_test.go | 97 ++++++++++++ ...rce_huaweicloud_live_url_authentication.go | 148 ++++++++++++++++++ 4 files changed, 326 insertions(+) create mode 100644 docs/resources/live_url_authentication.md create mode 100644 huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go create mode 100644 huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go diff --git a/docs/resources/live_url_authentication.md b/docs/resources/live_url_authentication.md new file mode 100644 index 0000000000..2262c20049 --- /dev/null +++ b/docs/resources/live_url_authentication.md @@ -0,0 +1,80 @@ +--- +subcategory: "Live" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_live_url_authentication" +description: |- + Manages an URL authentication resource within HuaweiCloud. +--- + +# huaweicloud_live_url_authentication + +Manages an URL authentication resource within HuaweiCloud. + +-> The current resource is a one-time resource, and destroying this resource will not change the current status. + +-> Before creating the resource, you need to cofiguration the URL validation first (creating + the URL validation resource first). + [documentation](https://support.huaweicloud.com/iLive-live/live_01_0049.html) + +## Example Usage + +```hcl +variable "domain_name" {} +variable "type" {} +variable "stream_name" {} +variable "app_name" {} + +resource "huaweicloud_live_url_authentication" "test" { + domain_name = var.domain_name + type = var.type + stream_name = var.stream_name + app_name = var.app_name +} +``` + +## 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 parameter will create a new resource. + +* `domain_name` - (Required, String, ForceNew) Specifies the domain name to which the URL validation belongs. + Including the ingest domain name and streaming domain name. + Changing this parameter will create a new resource. + +* `type` - (Required, String, ForceNew) Specifies the type of the domain name. + The valid values are as follow: + + **push**: Indicates ingest domain name. + + **pull**: Indicates streaming domain name. + + Changing this parameter will create a new resource. + +* `stream_name` - (Required, String, ForceNew) Specifies the stream name. + Changing this parameter will create a new resource. + +* `app_name` - (Required, String, ForceNew) Specifies the application name. + Changing this parameter will create a new resource. + +* `check_level` - (Optional, Int, ForceNew) Specifies the check level. + Changing this parameter will create a new resource. + The valid values are as follows: + + **3**: Indicates only checks the LiveID matches or not but not the validity of the signed URL. + + **5**: Indicates checks the LiveID matches or not and the validity of timestamp. + + -> 1.If the value of the `auth_type` in the URL validation is set to **c_aes**, this parameter is mandatory. +
2.The value of the LiveID consists of `app_name` and `stream_name`: /. + +* `start_time` - (Optional, String, ForceNew) Specifies the start time of the valid access time defined by the user. + Changing this parameter will create a new resource. + The time is in UTC, the format is **yyyy-mm-ddThh:mm:ssZ**. e.g. **2024-06-01T15:03:01Z** + If this parameter is not specified or is left empty, the current time is used by default. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID, UUID format. + +* `key_chain` - The generated signed URLs. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 31d9a82485..f42c3ca8c9 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -1859,6 +1859,7 @@ func Provider() *schema.Provider { "huaweicloud_live_referer_validation": live.ResourceRefererValidation(), "huaweicloud_live_snapshot": live.ResourceLiveSnapshot(), "huaweicloud_live_transcoding": live.ResourceTranscoding(), + "huaweicloud_live_url_authentication": live.ResourceUrlAuthentication(), "huaweicloud_live_url_validation": live.ResourceUrlValidation(), "huaweicloud_live_channel": live.ResourceChannel(), "huaweicloud_live_https_certificate": live.ResourceHTTPSCertificate(), diff --git a/huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go b/huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go new file mode 100644 index 0000000000..7c3207b4da --- /dev/null +++ b/huaweicloud/services/acceptance/live/resource_huaweicloud_live_url_authentication_test.go @@ -0,0 +1,97 @@ +package live + +import ( + "fmt" + "testing" + "time" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccUrlAuthentication_basic(t *testing.T) { + var ( + rName1 = "huaweicloud_live_url_authentication.test" + rName2 = "huaweicloud_live_url_authentication.try" + ingestDomainName = fmt.Sprintf("%s.huaweicloud.com", acceptance.RandomAccResourceNameWithDash()) + streamDomainName = fmt.Sprintf("%s.huaweicloud.com", acceptance.RandomAccResourceNameWithDash()) + startTime = time.Now().UTC().Add(24 * time.Hour).Format("2006-01-02T15:04:05Z") + ) + // Avoid CheckDestroy because this resource is a one-time action resource and there is nothing in the destroy method. + // lintignore:AT001 + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + // The action resource do not need to be checked and no processing is performed in the Read method. + Config: testAccUrlAuthentication_ingest(ingestDomainName, startTime), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(rName1, "key_chain.#", "1"), + ), + }, + { + // The action resource do not need to be checked and no processing is performed in the Read method. + Config: testAccUrlAuthentication_stream(streamDomainName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr(rName2, "key_chain.#", "3"), + ), + }, + }, + }) +} + +func testAccUrlAuthentication_ingest(name, startTime string) string { + return fmt.Sprintf(` +resource "huaweicloud_live_domain" "test" { + name = "%[1]s" + type = "push" +} + +resource "huaweicloud_live_url_validation" "test" { + domain_name = huaweicloud_live_domain.test.name + key = "IbBIzklRGCyMEd18oPV9sxAuuwNIzT81" + auth_type = "c_aes" + timeout = 1000 +} + +resource "huaweicloud_live_url_authentication" "test" { + domain_name = huaweicloud_live_domain.test.name + type = "push" + app_name = "live1" + stream_name = "tf-test" + check_level = 3 + start_time = "%[2]s" + + depends_on = [huaweicloud_live_url_validation.test] +} +`, name, startTime) +} + +func testAccUrlAuthentication_stream(name string) string { + return fmt.Sprintf(` +resource "huaweicloud_live_domain" "try" { + name = "%s" + type = "pull" +} + +resource "huaweicloud_live_url_validation" "try" { + domain_name = huaweicloud_live_domain.try.name + key = "IbBIzklRGCyMEd18oPV9sxAuuwNIzT81" + auth_type = "d_sha256" + timeout = 800 +} + +resource "huaweicloud_live_url_authentication" "try" { + domain_name = huaweicloud_live_domain.try.name + type = "pull" + app_name = "live2" + stream_name = "tf-try" + + depends_on = [huaweicloud_live_url_validation.try] +} +`, name) +} diff --git a/huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go b/huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go new file mode 100644 index 0000000000..5a9f7f81cb --- /dev/null +++ b/huaweicloud/services/live/resource_huaweicloud_live_url_authentication.go @@ -0,0 +1,148 @@ +package live + +import ( + "context" + "strings" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/go-uuid" + "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/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +// @API Live POST /v1/{project_id}/auth/chain +func ResourceUrlAuthentication() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceUrlAuthenticationCreate, + ReadContext: resourceUrlAuthenticationRead, + DeleteContext: resourceUrlAuthenticationDelete, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "domain_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Specifies the domain name to which the URL validation belongs.`, + }, + "type": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Specifies the type of the domain name.`, + }, + "stream_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Specifies the stream name.`, + }, + "app_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: `Specifies the application name.`, + }, + "check_level": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Description: `Specifies the check level.`, + }, + "start_time": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Description: `Specifies the start time of the valid access time defined by the user.`, + }, + "key_chain": { + Type: schema.TypeList, + Computed: true, + Elem: &schema.Schema{Type: schema.TypeString}, + Description: `The generated signed URLs.`, + }, + }, + } +} + +func buildSignUrlBodyParams(d *schema.ResourceData) map[string]interface{} { + return map[string]interface{}{ + "domain": d.Get("domain_name"), + "domain_type": d.Get("type"), + "stream": d.Get("stream_name"), + "app": d.Get("app_name"), + "check_level": utils.ValueIgnoreEmpty(d.Get("check_level")), + "start_time": utils.ValueIgnoreEmpty(d.Get("start_time")), + } +} + +func resourceUrlAuthenticationCreate(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var ( + cfg = meta.(*config.Config) + region = cfg.GetRegion(d) + httpUrl = "v1/{project_id}/auth/chain" + ) + + client, err := cfg.NewServiceClient("live", region) + if err != nil { + return diag.Errorf("error creating Live client: %s", err) + } + + createPath := client.Endpoint + httpUrl + createPath = strings.ReplaceAll(createPath, "{project_id}", client.ProjectID) + + createOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + JSONBody: utils.RemoveNil(buildSignUrlBodyParams(d)), + } + + resp, err := client.Request("POST", createPath, &createOpt) + if err != nil { + return diag.Errorf("error creating sign URL: %s", err) + } + + resourceId, err := uuid.GenerateUUID() + if err != nil { + return diag.Errorf("unable to generate ID: %s", err) + } + + d.SetId(resourceId) + + respBody, err := utils.FlattenResponse(resp) + if err != nil { + return diag.FromErr(err) + } + + keyChain := utils.PathSearch("keychain", respBody, make([]interface{}, 0)).([]interface{}) + if len(keyChain) == 0 { + return diag.Errorf("err creating sign URL, not found 'key_chain' in API response") + } + + var mErr *multierror.Error + mErr = multierror.Append( + mErr, + d.Set("key_chain", keyChain), + ) + + return diag.FromErr(mErr.ErrorOrNil()) +} + +func resourceUrlAuthenticationRead(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + // This resource is only a one-time action resource for generating a signer URL + return nil +} + +func resourceUrlAuthenticationDelete(_ context.Context, _ *schema.ResourceData, _ interface{}) diag.Diagnostics { + // This resource is only a one-time action resource for generating a signer URL + return nil +}