From 4116cf3f5aed8a42039a13e938050a51b0c25bde Mon Sep 17 00:00:00 2001
From: shichangkuo <shichangkuo90@gmail.com>
Date: Wed, 20 Mar 2024 11:00:54 +0800
Subject: [PATCH] feat(obs/bucket): support kms_key_project_id and logging
 agency params (#1114)

* feat(obs): support kms_key_project_id param

* feat(obs): support logging agency
---
 docs/resources/obs_bucket.md                  | 16 ++++-
 .../resource_flexibleengine_obs_bucket.go     | 61 ++++++++++++++-----
 ...resource_flexibleengine_obs_bucket_test.go | 13 ++--
 3 files changed, 71 insertions(+), 19 deletions(-)

diff --git a/docs/resources/obs_bucket.md b/docs/resources/obs_bucket.md
index 705bc84e3..7758dd3fb 100644
--- a/docs/resources/obs_bucket.md
+++ b/docs/resources/obs_bucket.md
@@ -33,6 +33,9 @@ resource "flexibleengine_obs_bucket" "b" {
 ### Enable Logging
 
 ```hcl
+# The agency must be an OBS cloud service agency with the `PutObject` permission.
+variable "agency_name" {}
+
 resource "flexibleengine_obs_bucket" "log_bucket" {
   bucket = "my-tf-log-bucket"
   acl    = "log-delivery-write"
@@ -45,6 +48,7 @@ resource "flexibleengine_obs_bucket" "b" {
   logging {
     target_bucket = flexibleengine_obs_bucket.log_bucket.id
     target_prefix = "log/"
+    agency        = var.agency_name
   }
 }
 ```
@@ -165,7 +169,10 @@ The following arguments are supported:
 
 * `encryption` - (Optional, Bool) Whether enable default server-side encryption of the bucket in SSE-KMS mode.
 
-* `kms_key_id` - (Optional, String) Specifies the ID of a kms key. If omitted, the default master key will be used.
+* `kms_key_id` - (Optional, String) Specifies the ID of a KMS key. If omitted, the default master key will be used.
+
+* `kms_key_project_id` - (Optional, String) Specifies the project ID to which the KMS key belongs. This field is valid
+  only when `kms_key_id` is specified.
 
 * `logging` - (Optional, List) A settings of bucket logging. The [logging](#obs_logging) object structure is documented
   below.
@@ -192,8 +199,15 @@ The `logging` object supports:
 
 * `target_bucket` - (Required, String) The name of the bucket that will receive the log objects.
   The acl policy of the target bucket should be `log-delivery-write`.
+
 * `target_prefix` - (Optional, String) To specify a key prefix for log objects.
 
+* `agency` - (Required, String) Specifies the IAM agency of OBS cloud service.
+
+  -> The IAM agency requires the `PutObject` permission for the target bucket.  If default encryption is enabled for the
+  target bucket, the agency also requires the `KMS Administrator` permission in the region where the target bucket is
+  located.
+
 <a name="obs_website"></a>
 The `website` object supports:
 
diff --git a/flexibleengine/resource_flexibleengine_obs_bucket.go b/flexibleengine/resource_flexibleengine_obs_bucket.go
index 4dda44878..5b8508f6b 100644
--- a/flexibleengine/resource_flexibleengine_obs_bucket.go
+++ b/flexibleengine/resource_flexibleengine_obs_bucket.go
@@ -64,6 +64,12 @@ func resourceObsBucket() *schema.Resource {
 							Optional: true,
 							Default:  "logs/",
 						},
+						"agency": {
+							Type:        schema.TypeString,
+							Optional:    true,
+							Computed:    true,
+							Description: "schema: Required",
+						},
 					},
 				},
 			},
@@ -239,6 +245,11 @@ func resourceObsBucket() *schema.Resource {
 				Type:     schema.TypeString,
 				Optional: true,
 			},
+			"kms_key_project_id": {
+				Type:     schema.TypeString,
+				Optional: true,
+				Computed: true,
+			},
 			"multi_az": {
 				Type:     schema.TypeBool,
 				Optional: true,
@@ -293,10 +304,15 @@ func resourceObsBucketCreate(d *schema.ResourceData, meta interface{}) error {
 }
 
 func resourceObsBucketUpdate(d *schema.ResourceData, meta interface{}) error {
-	config := meta.(*Config)
-	obsClient, err := config.ObjectStorageClient(GetRegion(d, config))
+	conf := meta.(*Config)
+	region := conf.GetRegion(d)
+	obsClient, err := conf.ObjectStorageClient(region)
 	if err != nil {
-		return fmt.Errorf("Error creating FlexibleEngine OBS client: %s", err)
+		return fmt.Errorf("error creating OBS client: %s", err)
+	}
+	obsClientWithSignature, err := conf.ObjectStorageClientWithSignature(region)
+	if err != nil {
+		return fmt.Errorf("error creating OBS client with signature: %s", err)
 	}
 
 	log.Printf("[DEBUG] Update OBS bucket %s", d.Id())
@@ -318,14 +334,14 @@ func resourceObsBucketUpdate(d *schema.ResourceData, meta interface{}) error {
 		}
 	}
 
-	if d.HasChanges("encryption", "kms_key_id") {
-		if err := resourceObsBucketEncryptionUpdate(obsClient, d); err != nil {
+	if d.HasChanges("encryption", "kms_key_id", "kms_key_project_id") {
+		if err := resourceObsBucketEncryptionUpdate(obsClientWithSignature, d); err != nil {
 			return err
 		}
 	}
 
 	if d.HasChange("logging") {
-		if err := resourceObsBucketLoggingUpdate(obsClient, d); err != nil {
+		if err := resourceObsBucketLoggingUpdate(obsClientWithSignature, d); err != nil {
 			return err
 		}
 	}
@@ -352,11 +368,15 @@ func resourceObsBucketUpdate(d *schema.ResourceData, meta interface{}) error {
 }
 
 func resourceObsBucketRead(d *schema.ResourceData, meta interface{}) error {
-	config := meta.(*Config)
-	region := GetRegion(d, config)
-	obsClient, err := config.ObjectStorageClient(region)
+	conf := meta.(*Config)
+	region := conf.GetRegion(d)
+	obsClient, err := conf.ObjectStorageClient(region)
 	if err != nil {
-		return fmt.Errorf("Error creating FlexibleEngine OBS client: %s", err)
+		return fmt.Errorf("error creating OBS client: %s", err)
+	}
+	obsClientWithSignature, err := conf.ObjectStorageClientWithSignature(region)
+	if err != nil {
+		return fmt.Errorf("error creating OBS client with signature: %s", err)
 	}
 
 	log.Printf("[DEBUG] Read OBS bucket: %s", d.Id())
@@ -394,12 +414,12 @@ func resourceObsBucketRead(d *schema.ResourceData, meta interface{}) error {
 	}
 
 	// Read the encryption configuration
-	if err := setObsBucketEncryption(obsClient, d); err != nil {
+	if err := setObsBucketEncryption(obsClientWithSignature, d); err != nil {
 		return err
 	}
 
 	// Read the logging configuration
-	if err := setObsBucketLogging(obsClient, d); err != nil {
+	if err := setObsBucketLogging(obsClientWithSignature, d); err != nil {
 		return err
 	}
 
@@ -548,6 +568,10 @@ func resourceObsBucketLoggingUpdate(obsClient *obs.ObsClient, d *schema.Resource
 		if val := c["target_prefix"].(string); val != "" {
 			loggingStatus.TargetPrefix = val
 		}
+
+		if val := c["agency"].(string); val != "" {
+			loggingStatus.Agency = val
+		}
 	}
 	log.Printf("[DEBUG] set logging of OBS bucket %s: %#v", bucket, loggingStatus)
 
@@ -565,8 +589,11 @@ func resourceObsBucketEncryptionUpdate(obsClient *obs.ObsClient, d *schema.Resou
 	if d.Get("encryption").(bool) {
 		input := &obs.SetBucketEncryptionInput{}
 		input.Bucket = bucket
-		input.SSEAlgorithm = obs.DEFAULT_SSE_KMS_ENCRYPTION
-		input.KMSMasterKeyID = d.Get("kms_key_id").(string)
+		input.SSEAlgorithm = obs.DEFAULT_SSE_KMS_ENCRYPTION_OBS
+		if raw, ok := d.GetOk("kms_key_id"); ok {
+			input.KMSMasterKeyID = raw.(string)
+			input.ProjectID = d.Get("kms_key_project_id").(string)
+		}
 
 		log.Printf("[DEBUG] enable default encryption of OBS bucket %s: %#v", bucket, input)
 		_, err := obsClient.SetBucketEncryption(input)
@@ -905,6 +932,7 @@ func setObsBucketEncryption(obsClient *obs.ObsClient, d *schema.ResourceData) er
 			if obsError.Code == "NoSuchEncryptionConfiguration" || obsError.Code == "FsNotSupport" {
 				d.Set("encryption", false)
 				d.Set("kms_key_id", nil)
+				d.Set("kms_key_project_id", nil)
 				return nil
 			}
 			return fmt.Errorf("Error getting encryption configuration of OBS bucket %s: %s,\n Reason: %s",
@@ -916,9 +944,11 @@ func setObsBucketEncryption(obsClient *obs.ObsClient, d *schema.ResourceData) er
 	if output.SSEAlgorithm != "" {
 		d.Set("encryption", true)
 		d.Set("kms_key_id", output.KMSMasterKeyID)
+		d.Set("kms_key_project_id", output.ProjectID)
 	} else {
 		d.Set("encryption", false)
 		d.Set("kms_key_id", nil)
+		d.Set("kms_key_project_id", nil)
 	}
 
 	return nil
@@ -939,6 +969,9 @@ func setObsBucketLogging(obsClient *obs.ObsClient, d *schema.ResourceData) error
 		if output.TargetPrefix != "" {
 			logging["target_prefix"] = output.TargetPrefix
 		}
+		if output.Agency != "" {
+			logging["agency"] = output.Agency
+		}
 		lcList = append(lcList, logging)
 	}
 	log.Printf("[DEBUG] saving logging configuration of OBS bucket: %s: %#v", bucket, lcList)
diff --git a/flexibleengine/resource_flexibleengine_obs_bucket_test.go b/flexibleengine/resource_flexibleengine_obs_bucket_test.go
index a4b3b1db1..5d5f590f8 100644
--- a/flexibleengine/resource_flexibleengine_obs_bucket_test.go
+++ b/flexibleengine/resource_flexibleengine_obs_bucket_test.go
@@ -143,7 +143,7 @@ func TestAccObsBucket_logging(t *testing.T) {
 				Config: testAccObsBucketConfigWithLogging(rInt),
 				Check: resource.ComposeTestCheckFunc(
 					testAccCheckObsBucketExists(resourceName),
-					testAccCheckObsBucketLogging(resourceName, target_bucket, "log/"),
+					testAccCheckObsBucketLogging(resourceName, target_bucket, "log/", "OBS"),
 				),
 			},
 		},
@@ -311,7 +311,7 @@ func testAccCheckObsBucketExists(n string) resource.TestCheckFunc {
 	}
 }
 
-func testAccCheckObsBucketLogging(name, target, prefix string) resource.TestCheckFunc {
+func testAccCheckObsBucketLogging(name, target, prefix, agency string) resource.TestCheckFunc {
 	return func(s *terraform.State) error {
 		rs, ok := s.RootModule().Resources[name]
 		if !ok {
@@ -319,9 +319,9 @@ func testAccCheckObsBucketLogging(name, target, prefix string) resource.TestChec
 		}
 
 		config := testAccProvider.Meta().(*Config)
-		obsClient, err := config.ObjectStorageClient(OS_REGION_NAME)
+		obsClient, err := config.ObjectStorageClientWithSignature(OS_REGION_NAME)
 		if err != nil {
-			return fmt.Errorf("Error creating FlexibleEngine OBS client: %s", err)
+			return fmt.Errorf("error creating OBS client with signature: %s", err)
 		}
 
 		output, err := obsClient.GetBucketLoggingConfiguration(rs.Primary.ID)
@@ -337,6 +337,10 @@ func testAccCheckObsBucketLogging(name, target, prefix string) resource.TestChec
 			return fmt.Errorf("%s.logging: Attribute 'target_prefix' expected %s, got %s",
 				name, output.TargetPrefix, prefix)
 		}
+		if output.Agency != agency {
+			return fmt.Errorf("%s.logging: Attribute 'agency' expected %s, got %s",
+				name, output.Agency, agency)
+		}
 
 		return nil
 	}
@@ -430,6 +434,7 @@ resource "flexibleengine_obs_bucket" "bucket" {
   logging {
     target_bucket = flexibleengine_obs_bucket.log_bucket.id
     target_prefix = "log/"
+	agency        = "OBS" # Make sure that the agency has the 'PutObject' permission.
   }
 }
 `, randInt, randInt)