From dfe5e32666f9ffe487524a98be808d87f90bee12 Mon Sep 17 00:00:00 2001 From: Zippo-Wang <852420284@qq.com> Date: Thu, 21 Aug 2025 09:20:35 +0800 Subject: [PATCH] feat(cts): add new data source to query resource tags --- docs/data-sources/cts_resource_tags.md | 49 +++++++ huaweicloud/provider.go | 1 + ...urce_huaweicloud_cts_resource_tags_test.go | 81 ++++++++++++ ...a_source_huaweicloud_cts_resources_tags.go | 123 ++++++++++++++++++ 4 files changed, 254 insertions(+) create mode 100644 docs/data-sources/cts_resource_tags.md create mode 100644 huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_resource_tags_test.go create mode 100644 huaweicloud/services/cts/data_source_huaweicloud_cts_resources_tags.go diff --git a/docs/data-sources/cts_resource_tags.md b/docs/data-sources/cts_resource_tags.md new file mode 100644 index 00000000000..13847460175 --- /dev/null +++ b/docs/data-sources/cts_resource_tags.md @@ -0,0 +1,49 @@ +--- +subcategory: "Cloud Trace Service (CTS)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_cts_resource_tags" +description: |- + Use this data source to get resource tag list of CTS service within HuaweiCloud. +--- + +# huaweicloud_cts_resource_tags + +Use this data source to get resource tag list of CTS service within HuaweiCloud. + +## Example Usage + +```hcl +variable "tracker_id" {} + +data "huaweicloud_cts_resource_tags" "test" { + resource_id = var.tracker_id +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String) Specifies the region in which to query the resource tags. + If omitted, the provider-level region will be used. + +* `resource_type` - (Required, String) Specifies the resource type to be queried. + The valid value is **cts-tracker**. + +* `resource_id` - (Required, String) Specifies the resource ID to be queried. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID. + +* `tags` - The list of tags that matched filter parameters. + The [tags](#cts_resource_tags_attr) structure is documented below. + + +The `tags` block supports: + +* `key` - The tag key. + +* `value` - The tag value. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 17bd49abb84..119c1c96a47 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -768,6 +768,7 @@ func Provider() *schema.Provider { "huaweicloud_cts_operations": cts.DataSourceCtsOperations(), "huaweicloud_cts_quotas": cts.DataSourceCtsQuotas(), "huaweicloud_cts_resources": cts.DataSourceCtsResources(), + "huaweicloud_cts_resource_tags": cts.DataSourceCtsResourcesTags(), "huaweicloud_cts_tags": cts.DataSourceCtsTags(), "huaweicloud_cts_users": cts.DataSourceCtsUsers(), diff --git a/huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_resource_tags_test.go b/huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_resource_tags_test.go new file mode 100644 index 00000000000..dc19c766076 --- /dev/null +++ b/huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_resource_tags_test.go @@ -0,0 +1,81 @@ +package cts + +import ( + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/services/acceptance" +) + +func TestAccDataSourceResourceTags_basic(t *testing.T) { + dataSourceName := "data.huaweicloud_cts_resource_tags.test" + dc := acceptance.InitDataSourceCheck(dataSourceName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testAccDataSourceResourceTags_basic(), + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttrSet(dataSourceName, "region"), + resource.TestCheckResourceAttrSet(dataSourceName, "tags.#"), + resource.TestCheckOutput("is_tags_exist", "true"), + resource.TestCheckOutput("is_key_set", "true"), + resource.TestCheckOutput("is_values_set", "true"), + ), + }, + }, + }) +} + +func testAccDataSourceResourceTags_base() string { + return ` +resource "huaweicloud_obs_bucket" "test" { + bucket = "tf-test-bucket" + acl = "private" + force_destroy = true +} + +resource "huaweicloud_cts_tracker" "test" { + bucket_name = huaweicloud_obs_bucket.test.bucket + file_prefix = "cts" + + tags = { + foo1 = "bar1", + foo2 = "bar2" + } +} +` +} +func testAccDataSourceResourceTags_basic() string { + return fmt.Sprintf(` +%s + +data "huaweicloud_cts_resource_tags" "test" { + resource_id = huaweicloud_cts_tracker.test.id + resource_type = "cts-tracker" +} + +locals { + tags = data.huaweicloud_cts_resource_tags.test.tags +} + +output "is_tags_exist" { + value = length(local.tags) > 0 +} + +output "is_key_set" { + value = alltrue([for v in local.tags[*].key : v != ""]) +} + +output "is_values_set" { + value = alltrue([for v in local.tags[*].value : v != ""]) +} +`, testAccDataSourceResourceTags_base()) +} diff --git a/huaweicloud/services/cts/data_source_huaweicloud_cts_resources_tags.go b/huaweicloud/services/cts/data_source_huaweicloud_cts_resources_tags.go new file mode 100644 index 00000000000..f3c81ffbd00 --- /dev/null +++ b/huaweicloud/services/cts/data_source_huaweicloud_cts_resources_tags.go @@ -0,0 +1,123 @@ +package cts + +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 CTS GET /v3/{project_id}/{resource_type}/{resource_id}/tags +func DataSourceCtsResourcesTags() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCtsResourcesTagsRead, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `The region in which to query the resource tags.`, + }, + "resource_type": { + Type: schema.TypeString, + Required: true, + Description: `The resource type to be queried.`, + }, + "resource_id": { + Type: schema.TypeString, + Required: true, + Description: `The resource ID to be queried.`, + }, + "tags": { + Type: schema.TypeList, + Computed: true, + Description: `The list of tags that matched filter parameters.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "key": { + Type: schema.TypeString, + Computed: true, + Description: `The tag key.`, + }, + "value": { + Type: schema.TypeString, + Computed: true, + Description: `The tag value.`, + }, + }, + }, + }, + }, + } +} + +func dataSourceCtsResourcesTagsRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + cfg := meta.(*config.Config) + region := cfg.GetRegion(d) + client, err := cfg.NewServiceClient("cts", region) + if err != nil { + return diag.Errorf("error creating CTS client: %s", err) + } + + resourceId := d.Get("resource_id").(string) + httpUrl := "v3/{project_id}/{resource_type}/{resource_id}/tags" + path := client.Endpoint + httpUrl + path = strings.ReplaceAll(path, "{project_id}", client.ProjectID) + path = strings.ReplaceAll(path, "{resource_type}", d.Get("resource_type").(string)) + path = strings.ReplaceAll(path, "{resource_id}", resourceId) + + requestOpt := golangsdk.RequestOpts{ + KeepResponseBody: true, + MoreHeaders: map[string]string{ + "Content-Type": "application/json", + }, + } + + response, err := client.Request("GET", path, &requestOpt) + if err != nil { + return diag.Errorf("error retrieving CTS resource tags: %s", err) + } + + respBody, err := utils.FlattenResponse(response) + if err != nil { + return diag.FromErr(err) + } + + randUUID, err := uuid.GenerateUUID() + if err != nil { + return diag.Errorf("unable to generate ID: %s", err) + } + d.SetId(randUUID) + + mErr := multierror.Append(nil, + d.Set("region", region), + d.Set("tags", flattenResourceTags(utils.PathSearch("tags", respBody, make([]interface{}, 0)).([]interface{}))), + ) + + return diag.FromErr(mErr.ErrorOrNil()) +} + +func flattenResourceTags(tags []interface{}) []interface{} { + if len(tags) == 0 { + return nil + } + + result := make([]interface{}, 0, len(tags)) + for _, tag := range tags { + result = append(result, map[string]interface{}{ + "key": utils.PathSearch("key", tag, nil), + "value": utils.PathSearch("value", tag, nil), + }) + } + + return result +}