diff --git a/docs/data-sources/cts_check_bucket.md b/docs/data-sources/cts_check_bucket.md new file mode 100644 index 00000000000..1a35f85e060 --- /dev/null +++ b/docs/data-sources/cts_check_bucket.md @@ -0,0 +1,70 @@ +--- +subcategory: "Cloud Trace Service (CTS)" +layout: "huaweicloud" +page_title: "HuaweiCloud: huaweicloud_cts_check_bucket" +description: |- + Use this data source to check whether data can be transferred to the OBS bucket. +--- + +# huaweicloud_cts_check_bucket + +Use this data source to check whether data can be transferred to the OBS bucket. + +## Example Usage + +```hcl +data "huaweicloud_cts_check_bucket" "test" { + bucket_name = "my_bucket" + bucket_location = "cn-north-4" +} +``` + +## Argument Reference + +The following arguments are supported: + +* `region` - (Optional, String) Specifies the region in which to query the resource. + If omitted, the provider-level region will be used. + +* `bucket_name` - (Required, String) Specifies the OBS bucket name. + +* `bucket_location` - (Required, String) Specifies the OBS bucket location. + +* `is_support_trace_files_encryption` - (Optional, Bool) Specifies whether trace files are encrypted during transfer to + an OBS bucket. + +* `kms_id` - (Optional, String) Specifies the Key ID used for encrypting transferred trace files. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The data source ID. + +* `buckets` - The OBS bucket information. + The [buckets](#BucketsAttr) structure is documented below. + + +The `buckets` block supports: + +* `bucket_name` - The OBS bucket name. + +* `bucket_location` - The OBS bucket location. + +* `is_support_trace_files_encryption` - Whether trace files are encrypted during transfer to an OBS bucket. + +* `kms_id` - The Key ID used for encrypting transferred trace files. + +* `check_bucket_response` - The check result of the OBS bucket. + The [check_bucket_response](#CheckBucketResponse) structure is documented below. + + +The `check_bucket_response` block supports: + +* `error_code` - The error code. + +* `error_message` - The error message. + +* `response_code` - The returned HTTP status code. + +* `success` - Whether the transfer is successful. diff --git a/huaweicloud/provider.go b/huaweicloud/provider.go index 4637616f4fb..29fa845300b 100644 --- a/huaweicloud/provider.go +++ b/huaweicloud/provider.go @@ -628,6 +628,7 @@ func Provider() *schema.Provider { "huaweicloud_codearts_inspector_host_groups": codeartsinspector.DataSourceCodeartsInspectorHostGroups(), "huaweicloud_codearts_inspector_hosts": codeartsinspector.DataSourceCodeartsInspectorHosts(), + "huaweicloud_cts_check_bucket": cts.DataSourceCheckBucket(), "huaweicloud_cts_notifications": cts.DataSourceNotifications(), "huaweicloud_cts_traces": cts.DataSourceCtsTraces(), "huaweicloud_cts_trackers": cts.DataSourceCtsTrackers(), diff --git a/huaweicloud/services/acceptance/acceptance.go b/huaweicloud/services/acceptance/acceptance.go index d712972c94e..aa0d97e49c6 100644 --- a/huaweicloud/services/acceptance/acceptance.go +++ b/huaweicloud/services/acceptance/acceptance.go @@ -300,6 +300,7 @@ var ( HW_CFW_PREDEFINED_ADDRESS_GROUP2 = os.Getenv("HW_CFW_PREDEFINED_ADDRESS_GROUP2") HW_CFW_IPS_CUSTOM_RULE = os.Getenv("HW_CFW_IPS_CUSTOM_RULE") + HW_CTS_KMS_ID = os.Getenv("HW_CTS_KMS_ID") HW_CTS_START_TIME = os.Getenv("HW_CTS_START_TIME") HW_CTS_END_TIME = os.Getenv("HW_CTS_END_TIME") @@ -1569,6 +1570,13 @@ func TestAccPreCheckCCConnectionRouteRegionName(t *testing.T) { } } +// lintignore:AT003 +func TestAccPreCheckCtsKmsId(t *testing.T) { + if HW_CTS_KMS_ID == "" { + t.Skip("HW_CTS_KMS_ID must be set for CTS acceptance tests") + } +} + // lintignore:AT003 func TestAccPreCheckCtsTimeRange(t *testing.T) { if HW_CTS_START_TIME == "" || HW_CTS_END_TIME == "" { diff --git a/huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_check_bucket_test.go b/huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_check_bucket_test.go new file mode 100644 index 00000000000..a414c3708dc --- /dev/null +++ b/huaweicloud/services/acceptance/cts/data_source_huaweicloud_cts_check_bucket_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 TestAccDataSourceCheckBucket_basic(t *testing.T) { + dataSource := "data.huaweicloud_cts_check_bucket.test" + rName := acceptance.RandomAccResourceNameWithDash() + dc := acceptance.InitDataSourceCheck(dataSource) + baseConfig := testDataSourceCheckBucket_base(rName) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + acceptance.TestAccPreCheck(t) + acceptance.TestAccPreCheckCtsKmsId(t) + }, + ProviderFactories: acceptance.TestAccProviderFactories, + Steps: []resource.TestStep{ + { + Config: testDataSourceCheckBucket_basic(baseConfig), + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttr(dataSource, "buckets.0.bucket_name", rName), + resource.TestCheckResourceAttr(dataSource, "buckets.0.check_bucket_response.0.response_code", "200"), + resource.TestCheckResourceAttr(dataSource, "buckets.0.check_bucket_response.0.success", "true"), + ), + }, + { + Config: testDataSourceCheckBucket_KmsId(baseConfig), + Check: resource.ComposeTestCheckFunc( + dc.CheckResourceExists(), + resource.TestCheckResourceAttr(dataSource, "buckets.0.bucket_name", rName), + resource.TestCheckResourceAttr(dataSource, "buckets.0.check_bucket_response.0.response_code", "200"), + resource.TestCheckResourceAttr(dataSource, "buckets.0.check_bucket_response.0.success", "true"), + resource.TestCheckResourceAttr(dataSource, "buckets.0.is_support_trace_files_encryption", "true"), + resource.TestCheckResourceAttr(dataSource, "buckets.0.kms_id", acceptance.HW_CTS_KMS_ID), + ), + }, + }, + }) +} + +func testDataSourceCheckBucket_basic(config string) string { + return fmt.Sprintf(` +%s + +data "huaweicloud_cts_check_bucket" "test" { + bucket_name = huaweicloud_obs_bucket.bucket.bucket + bucket_location = huaweicloud_obs_bucket.bucket.region +} +`, config) +} + +func testDataSourceCheckBucket_KmsId(config string) string { + return fmt.Sprintf(` +%[1]s + +data "huaweicloud_cts_check_bucket" "test" { + bucket_name = huaweicloud_obs_bucket.bucket.bucket + bucket_location = huaweicloud_obs_bucket.bucket.region + kms_id = "%[2]s" + is_support_trace_files_encryption = true +} +`, config, acceptance.HW_CTS_KMS_ID) +} + +func testDataSourceCheckBucket_base(rName string) string { + return fmt.Sprintf(` +resource "huaweicloud_obs_bucket" "bucket" { + bucket = "%s" + acl = "public-read" + force_destroy = true +} +`, rName) +} diff --git a/huaweicloud/services/cts/data_source_huaweicloud_cts_check_bucket.go b/huaweicloud/services/cts/data_source_huaweicloud_cts_check_bucket.go new file mode 100644 index 00000000000..774916e4fb6 --- /dev/null +++ b/huaweicloud/services/cts/data_source_huaweicloud_cts_check_bucket.go @@ -0,0 +1,198 @@ +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/tidwall/gjson" + + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/config" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/helper/httphelper" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/helper/schemas" + "github.com/huaweicloud/terraform-provider-huaweicloud/huaweicloud/utils" +) + +func DataSourceCheckBucket() *schema.Resource { + return &schema.Resource{ + ReadContext: dataSourceCtsCheckBucketRead, + + Schema: map[string]*schema.Schema{ + "region": { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: `Specifies the region in which to query the resource. If omitted, the provider-level region will be used.`, + }, + "bucket_name": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the OBS bucket name.`, + }, + "bucket_location": { + Type: schema.TypeString, + Required: true, + Description: `Specifies the OBS bucket location.`, + }, + "kms_id": { + Type: schema.TypeString, + Optional: true, + Description: `Specifies the Key ID used for encrypting transferred trace files.`, + }, + "is_support_trace_files_encryption": { + Type: schema.TypeBool, + Optional: true, + Description: `Specifies whether trace files are encrypted during transfer to an OBS bucket.`, + }, + "buckets": { + Type: schema.TypeList, + Computed: true, + Description: `The OBS bucket information.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "bucket_name": { + Type: schema.TypeString, + Computed: true, + Description: `The OBS bucket name.`, + }, + "bucket_location": { + Type: schema.TypeString, + Computed: true, + Description: `The OBS bucket location.`, + }, + "kms_id": { + Type: schema.TypeString, + Computed: true, + Description: `The Key ID used for encrypting transferred trace files.`, + }, + "is_support_trace_files_encryption": { + Type: schema.TypeBool, + Computed: true, + Description: `Whether trace files are encrypted during transfer to an OBS bucket.`, + }, + "check_bucket_response": { + Type: schema.TypeList, + Computed: true, + Description: `The check result of the OBS bucket.`, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "error_code": { + Type: schema.TypeString, + Computed: true, + Description: `The error code.`, + }, + "error_message": { + Type: schema.TypeString, + Computed: true, + Description: `The error message.`, + }, + "response_code": { + Type: schema.TypeInt, + Computed: true, + Description: `The returned HTTP status code.`, + }, + "success": { + Type: schema.TypeBool, + Computed: true, + Description: `Whether the transfer is successful.`, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +type CheckBucketDSWrapper struct { + *schemas.ResourceDataWrapper + Config *config.Config +} + +func newCheckBucketDSWrapper(d *schema.ResourceData, meta interface{}) *CheckBucketDSWrapper { + return &CheckBucketDSWrapper{ + ResourceDataWrapper: schemas.NewSchemaWrapper(d), + Config: meta.(*config.Config), + } +} + +func dataSourceCtsCheckBucketRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + wrapper := newCheckBucketDSWrapper(d, meta) + checkObsBucketsRst, err := wrapper.CheckObsBuckets() + if err != nil { + return diag.FromErr(err) + } + + id, err := uuid.GenerateUUID() + if err != nil { + return diag.FromErr(err) + } + d.SetId(id) + + err = wrapper.checkObsBucketsToSchema(checkObsBucketsRst) + if err != nil { + return diag.FromErr(err) + } + + return nil +} + +// @API CTS POST /v3/{domain_id}/checkbucket +func (w *CheckBucketDSWrapper) CheckObsBuckets() (*gjson.Result, error) { + client, err := w.NewClient(w.Config, "cts") + if err != nil { + return nil, err + } + + uri := "/v3/{domain_id}/checkbucket" + uri = strings.ReplaceAll(uri, "{domain_id}", w.Config.DomainID) + params := map[string]any{ + "bucket_location": w.Get("bucket_location"), + "bucket_name": w.Get("bucket_name"), + "is_support_trace_files_encryption": w.Get("is_support_trace_files_encryption"), + "kms_id": w.Get("kms_id"), + } + params = utils.RemoveNil(params) + reqBody := map[string]any{ + "buckets": []map[string]any{params}, + } + return httphelper.New(client). + Method("POST"). + URI(uri). + Body(reqBody). + Request(). + Result() +} + +func (w *CheckBucketDSWrapper) checkObsBucketsToSchema(body *gjson.Result) error { + d := w.ResourceData + mErr := multierror.Append(nil, + d.Set("region", w.Config.GetRegion(w.ResourceData)), + d.Set("buckets", schemas.SliceToList(body.Get("buckets"), + func(buckets gjson.Result) any { + return map[string]any{ + "bucket_name": buckets.Get("bucket_name").Value(), + "bucket_location": buckets.Get("bucket_location").Value(), + "kms_id": buckets.Get("kms_id").Value(), + "is_support_trace_files_encryption": buckets.Get("is_support_trace_files_encryption").Value(), + "check_bucket_response": schemas.SliceToList(buckets.Get("check_bucket_response"), + func(checkBucketResponse gjson.Result) any { + return map[string]any{ + "error_code": checkBucketResponse.Get("error_code").Value(), + "error_message": checkBucketResponse.Get("error_message").Value(), + "response_code": checkBucketResponse.Get("response_code").Value(), + "success": checkBucketResponse.Get("success").Value(), + } + }, + ), + } + }, + )), + ) + return mErr.ErrorOrNil() +}