Skip to content

Commit 010585f

Browse files
testpod-source-phkantare
authored andcommitted
added support for cos retention
updated based on comment updated based on comment Set the attribute level based on comment Revert "Set the attribute level based on comment" This reverts commit e189b957e76fc1cbc79a3cad5f0f84e5a6804e19. Set the attribute level based on comment
1 parent 99e6733 commit 010585f

File tree

9 files changed

+329
-40
lines changed

9 files changed

+329
-40
lines changed

examples/ibm-cos-bucket/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,7 @@ data "ibm_cos_bucket" "standard-ams03" {
121121
| expire_ruleid | Unique identifier for the rule. | `string` | no
122122
| expire_days | Specifies the number of days when the specific expire rule action takes effect. | `int` | yes
123123
| expire_prefix | Specifies a prefix filter to apply to only a subset of objects with names that match the prefix. | `string` | no
124+
| default | Specifies a default retention period to apply in all objects in the bucket. | `int` | yes
125+
| maximum | Specifies maximum duration of time an object can be kept unmodified in the bucket. | `int` | yes
126+
| minimum | Specifies minimum duration of time an object must be kept unmodified in the bucket. | `int` | yes
127+
| permanent | Specifies a permanent retention status either enable or disable for a bucket. | `bool` | no

examples/ibm-cos-bucket/main.tf

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,10 @@ resource "ibm_cos_bucket" "lifecycle_rule_cos" {
5656
days = var.expire_days
5757
prefix = var.expire_prefix
5858
}
59-
}
59+
retention_rule {
60+
default = var.default_retention
61+
maximum = var.maximum_retention
62+
minimum = var.minimum_retention
63+
permanent = false
64+
}
65+
}

examples/ibm-cos-bucket/variables.tf

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,16 @@ variable "expire_days" {
4040

4141
variable "expire_prefix" {
4242
default = ""
43+
}
44+
45+
variable "default_retention" {
46+
default = "0"
47+
}
48+
49+
variable "minimum_retention" {
50+
default = "0"
51+
}
52+
53+
variable "maximum_retention" {
54+
default = "1"
4355
}

ibm/data_source_ibm_cos_bucket.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,44 @@ func dataSourceIBMCosBucket() *schema.Resource {
186186
},
187187
},
188188
},
189+
"retention_rule": {
190+
Type: schema.TypeList,
191+
Optional: true,
192+
MaxItems: 1000,
193+
Description: "A retention policy is enabled at the IBM Cloud Object Storage bucket level. Minimum, maximum and default retention period are defined by this policy and apply to all objects in the bucket.",
194+
ForceNew: true,
195+
Elem: &schema.Resource{
196+
Schema: map[string]*schema.Schema{
197+
"default": {
198+
Type: schema.TypeInt,
199+
Required: true,
200+
ValidateFunc: validateAllowedRangeInt(0, 365243),
201+
Description: "If an object is stored in the bucket without specifying a custom retention period.",
202+
ForceNew: false,
203+
},
204+
"maximum": {
205+
Type: schema.TypeInt,
206+
Required: true,
207+
ValidateFunc: validateAllowedRangeInt(0, 365243),
208+
Description: "Maximum duration of time an object can be kept unmodified in the bucket.",
209+
ForceNew: false,
210+
},
211+
"minimum": {
212+
Type: schema.TypeInt,
213+
Required: true,
214+
ValidateFunc: validateAllowedRangeInt(0, 365243),
215+
Description: "Minimum duration of time an object must be kept unmodified in the bucket",
216+
ForceNew: false,
217+
},
218+
"permanent": {
219+
Type: schema.TypeBool,
220+
Optional: true,
221+
Default: false,
222+
Description: "Enable or disable the permanent retention policy on the bucket",
223+
},
224+
},
225+
},
226+
},
189227
},
190228
}
191229
}
@@ -347,6 +385,20 @@ func dataSourceIBMCosBucketRead(d *schema.ResourceData, meta interface{}) error
347385
}
348386
}
349387

388+
// Read the retention policy
389+
retentionInput := &s3.GetBucketProtectionConfigurationInput{
390+
Bucket: aws.String(bucketName),
391+
}
392+
retentionptr, err := s3Client.GetBucketProtectionConfiguration(retentionInput)
393+
394+
if err != nil && bucketPtr != nil && bucketPtr.Firewall != nil && !strings.Contains(err.Error(), "AccessDenied: Access Denied") {
395+
return err
396+
}
397+
398+
if retentionptr != nil {
399+
d.Set("retention_rule", retentionRuleGet(retentionptr.ProtectionConfiguration))
400+
}
401+
350402
return nil
351403
}
352404

ibm/resource_ibm_cos_bucket.go

Lines changed: 143 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,44 @@ func resourceIBMCOS() *schema.Resource {
250250
},
251251
},
252252
},
253+
"retention_rule": {
254+
Type: schema.TypeList,
255+
Optional: true,
256+
MaxItems: 1000,
257+
Description: "A retention policy is enabled at the IBM Cloud Object Storage bucket level. Minimum, maximum and default retention period are defined by this policy and apply to all objects in the bucket.",
258+
ForceNew: true,
259+
Elem: &schema.Resource{
260+
Schema: map[string]*schema.Schema{
261+
"default": {
262+
Type: schema.TypeInt,
263+
Required: true,
264+
ValidateFunc: validateAllowedRangeInt(0, 365243),
265+
Description: "If an object is stored in the bucket without specifying a custom retention period.",
266+
ForceNew: false,
267+
},
268+
"maximum": {
269+
Type: schema.TypeInt,
270+
Required: true,
271+
ValidateFunc: validateAllowedRangeInt(0, 365243),
272+
Description: "Maximum duration of time an object can be kept unmodified in the bucket.",
273+
ForceNew: false,
274+
},
275+
"minimum": {
276+
Type: schema.TypeInt,
277+
Required: true,
278+
ValidateFunc: validateAllowedRangeInt(0, 365243),
279+
Description: "Minimum duration of time an object must be kept unmodified in the bucket",
280+
ForceNew: false,
281+
},
282+
"permanent": {
283+
Type: schema.TypeBool,
284+
Optional: true,
285+
Default: false,
286+
Description: "Enable or disable the permanent retention policy on the bucket",
287+
},
288+
},
289+
},
290+
},
253291
"force_delete": {
254292
Type: schema.TypeBool,
255293
Optional: true,
@@ -359,46 +397,45 @@ func expireRuleList(expireList []interface{}) []*s3.LifecycleRule {
359397
}
360398

361399
func resourceIBMCOSUpdate(d *schema.ResourceData, meta interface{}) error {
400+
var s3Conf *aws.Config
401+
rsConClient, err := meta.(ClientSession).BluemixSession()
402+
if err != nil {
403+
return err
404+
}
405+
bucketName := parseBucketId(d.Id(), "bucketName")
406+
serviceID := parseBucketId(d.Id(), "serviceID")
407+
endpointType := parseBucketId(d.Id(), "endpointType")
408+
apiEndpoint, apiEndpointPrivate := selectCosApi(parseBucketId(d.Id(), "apiType"), parseBucketId(d.Id(), "bLocation"))
409+
if endpointType == "private" {
410+
apiEndpoint = apiEndpointPrivate
411+
}
412+
authEndpoint, err := rsConClient.Config.EndpointLocator.IAMEndpoint()
413+
if err != nil {
414+
return err
415+
}
416+
authEndpointPath := fmt.Sprintf("%s%s", authEndpoint, "/identity/token")
417+
apiKey := rsConClient.Config.BluemixAPIKey
418+
if apiKey != "" {
419+
s3Conf = aws.NewConfig().WithEndpoint(envFallBack([]string{"IBMCLOUD_COS_ENDPOINT"}, apiEndpoint)).WithCredentials(ibmiam.NewStaticCredentials(aws.NewConfig(), authEndpointPath, apiKey, serviceID)).WithS3ForcePathStyle(true)
420+
}
421+
iamAccessToken := rsConClient.Config.IAMAccessToken
422+
if iamAccessToken != "" {
423+
initFunc := func() (*token.Token, error) {
424+
return &token.Token{
425+
AccessToken: rsConClient.Config.IAMAccessToken,
426+
RefreshToken: rsConClient.Config.IAMRefreshToken,
427+
TokenType: "Bearer",
428+
ExpiresIn: int64((time.Hour * 248).Seconds()) * -1,
429+
Expiration: time.Now().Add(-1 * time.Hour).Unix(),
430+
}, nil
431+
}
432+
s3Conf = aws.NewConfig().WithEndpoint(envFallBack([]string{"IBMCLOUD_COS_ENDPOINT"}, apiEndpoint)).WithCredentials(ibmiam.NewCustomInitFuncCredentials(aws.NewConfig(), initFunc, authEndpointPath, serviceID)).WithS3ForcePathStyle(true)
433+
}
434+
s3Sess := session.Must(session.NewSession())
435+
s3Client := s3.New(s3Sess, s3Conf)
362436

363437
//// Update the lifecycle (Archive or Expire)
364438
if d.HasChange("archive_rule") || d.HasChange("expire_rule") {
365-
var s3Conf *aws.Config
366-
rsConClient, err := meta.(ClientSession).BluemixSession()
367-
if err != nil {
368-
return err
369-
}
370-
bucketName := parseBucketId(d.Id(), "bucketName")
371-
serviceID := parseBucketId(d.Id(), "serviceID")
372-
endpointType := parseBucketId(d.Id(), "endpointType")
373-
apiEndpoint, apiEndpointPrivate := selectCosApi(parseBucketId(d.Id(), "apiType"), parseBucketId(d.Id(), "bLocation"))
374-
if endpointType == "private" {
375-
apiEndpoint = apiEndpointPrivate
376-
}
377-
authEndpoint, err := rsConClient.Config.EndpointLocator.IAMEndpoint()
378-
if err != nil {
379-
return err
380-
}
381-
authEndpointPath := fmt.Sprintf("%s%s", authEndpoint, "/identity/token")
382-
apiKey := rsConClient.Config.BluemixAPIKey
383-
if apiKey != "" {
384-
s3Conf = aws.NewConfig().WithEndpoint(envFallBack([]string{"IBMCLOUD_COS_ENDPOINT"}, apiEndpoint)).WithCredentials(ibmiam.NewStaticCredentials(aws.NewConfig(), authEndpointPath, apiKey, serviceID)).WithS3ForcePathStyle(true)
385-
}
386-
iamAccessToken := rsConClient.Config.IAMAccessToken
387-
if iamAccessToken != "" && apiKey == "" {
388-
initFunc := func() (*token.Token, error) {
389-
return &token.Token{
390-
AccessToken: rsConClient.Config.IAMAccessToken,
391-
RefreshToken: rsConClient.Config.IAMRefreshToken,
392-
TokenType: "Bearer",
393-
ExpiresIn: int64((time.Hour * 248).Seconds()) * -1,
394-
Expiration: time.Now().Add(-1 * time.Hour).Unix(),
395-
}, nil
396-
}
397-
s3Conf = aws.NewConfig().WithEndpoint(envFallBack([]string{"IBMCLOUD_COS_ENDPOINT"}, apiEndpoint)).WithCredentials(ibmiam.NewCustomInitFuncCredentials(aws.NewConfig(), initFunc, authEndpointPath, serviceID)).WithS3ForcePathStyle(true)
398-
}
399-
s3Sess := session.Must(session.NewSession())
400-
s3Client := s3.New(s3Sess, s3Conf)
401-
402439
var archive, archive_ok = d.GetOk("archive_rule")
403440
var expire, expire_ok = d.GetOk("expire_rule")
404441
var rules []*s3.LifecycleRule
@@ -434,11 +471,65 @@ func resourceIBMCOSUpdate(d *schema.ResourceData, meta interface{}) error {
434471
}
435472
}
436473

474+
//// Update the Retention policy
475+
if d.HasChange("retention_rule") {
476+
var defaultretention, minretention, maxretention int64
477+
var permanentretention bool
478+
if retention, ok := d.GetOk("retention_rule"); ok {
479+
retentionList := retention.([]interface{})
480+
if len(retentionList) > 1 {
481+
return fmt.Errorf("Can not more than 1 retention policy")
482+
}
483+
for _, l := range retentionList {
484+
retentionMap, _ := l.(map[string]interface{})
485+
//Default Days
486+
if defaultretentionSet, exist := retentionMap["default"]; exist {
487+
defaultdays := int64(defaultretentionSet.(int))
488+
defaultretention = defaultdays
489+
}
490+
//Maximum Days
491+
if maxretentionSet, exist := retentionMap["maximum"]; exist {
492+
maxdays := int64(maxretentionSet.(int))
493+
maxretention = maxdays
494+
}
495+
//Minimum Days
496+
if minretentionSet, exist := retentionMap["minimum"]; exist {
497+
mindays := int64(minretentionSet.(int))
498+
minretention = mindays
499+
}
500+
//Permanent Retention Enable/Disable
501+
if permanentretentionSet, exist := retentionMap["permanent"]; exist {
502+
permanentretention = permanentretentionSet.(bool)
503+
}
504+
}
505+
// PUT BUCKET PROTECTION CONFIGURATION
506+
pInput := &s3.PutBucketProtectionConfigurationInput{
507+
Bucket: aws.String(bucketName),
508+
ProtectionConfiguration: &s3.ProtectionConfiguration{
509+
DefaultRetention: &s3.BucketProtectionDefaultRetention{
510+
Days: aws.Int64(defaultretention),
511+
},
512+
MaximumRetention: &s3.BucketProtectionMaximumRetention{
513+
Days: aws.Int64(maxretention),
514+
},
515+
MinimumRetention: &s3.BucketProtectionMinimumRetention{
516+
Days: aws.Int64(minretention),
517+
},
518+
Status: aws.String("Retention"),
519+
EnablePermanentRetention: aws.Bool(permanentretention),
520+
},
521+
}
522+
_, err := s3Client.PutBucketProtectionConfiguration(pInput)
523+
if err != nil {
524+
return fmt.Errorf("failed to update the retention rule on COS bucket %s, %v", bucketName, err)
525+
}
526+
}
527+
}
528+
437529
sess, err := meta.(ClientSession).CosConfigV1API()
438530
if err != nil {
439531
return err
440532
}
441-
endpointType := parseBucketId(d.Id(), "endpointType")
442533
if endpointType == "private" {
443534
sess.SetServiceURL("https://config.private.cloud-object-storage.cloud.ibm.com/v1")
444535
}
@@ -447,7 +538,7 @@ func resourceIBMCOSUpdate(d *schema.ResourceData, meta interface{}) error {
447538
updateBucketConfigOptions := &resourceconfigurationv1.UpdateBucketConfigOptions{}
448539

449540
//BucketName
450-
bucketName := d.Get("bucket_name").(string)
541+
bucketName = d.Get("bucket_name").(string)
451542
updateBucketConfigOptions.Bucket = &bucketName
452543

453544
if d.HasChange("allowed_ip") {
@@ -673,6 +764,19 @@ func resourceIBMCOSRead(d *schema.ResourceData, meta interface{}) error {
673764
}
674765
}
675766

767+
retentionInput := &s3.GetBucketProtectionConfigurationInput{
768+
Bucket: aws.String(bucketName),
769+
}
770+
retentionptr, err := s3Client.GetBucketProtectionConfiguration(retentionInput)
771+
772+
if err != nil && bucketPtr != nil && bucketPtr.Firewall != nil && !strings.Contains(err.Error(), "AccessDenied: Access Denied") {
773+
return err
774+
}
775+
776+
if retentionptr != nil {
777+
d.Set("retention_rule", retentionRuleGet(retentionptr.ProtectionConfiguration))
778+
}
779+
676780
return nil
677781
}
678782

ibm/resource_ibm_cos_bucket_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,35 @@ func TestAccIBMCosBucket_Expire(t *testing.T) {
258258
})
259259
}
260260

261+
func TestAccIBMCosBucket_Retention(t *testing.T) {
262+
263+
cosServiceName := fmt.Sprintf("cos_instance_%d", acctest.RandIntRange(10, 100))
264+
bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100))
265+
bucketRegion := "jp-tok"
266+
bucketClass := "standard"
267+
bucketRegionType := "region_location"
268+
default_retention := 0
269+
maximum_retention := 1
270+
minimum_retention := 0
271+
272+
resource.Test(t, resource.TestCase{
273+
PreCheck: func() { testAccPreCheck(t) },
274+
Providers: testAccProviders,
275+
CheckDestroy: testAccCheckIBMCosBucketDestroy,
276+
Steps: []resource.TestStep{
277+
resource.TestStep{
278+
Config: testAccCheckIBMCosBucket_retention(cosServiceName, bucketName, bucketRegionType, bucketRegion, bucketClass, default_retention, maximum_retention, minimum_retention),
279+
Check: resource.ComposeAggregateTestCheckFunc(
280+
testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName),
281+
resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName),
282+
resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass),
283+
resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "region_location", bucketRegion),
284+
resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "retention_rule.#", "1"),
285+
),
286+
},
287+
},
288+
})
289+
}
261290
func TestAccIBMCosBucket_Smart_Type(t *testing.T) {
262291
serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100))
263292
bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100))
@@ -840,3 +869,33 @@ func testAccCheckIBMCosBucket_update_archive_expire(cosServiceName string, bucke
840869
}
841870
`, cosServiceName, bucketName, region, storageClass)
842871
}
872+
873+
func testAccCheckIBMCosBucket_retention(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, default_retention int, maximum_retention int, minimum_retention int) string {
874+
875+
return fmt.Sprintf(`
876+
data "ibm_resource_group" "cos_group" {
877+
name = "Default"
878+
}
879+
880+
resource "ibm_resource_instance" "instance" {
881+
name = "%s"
882+
service = "cloud-object-storage"
883+
plan = "standard"
884+
location = "global"
885+
resource_group_id = data.ibm_resource_group.cos_group.id
886+
}
887+
888+
resource "ibm_cos_bucket" "bucket" {
889+
bucket_name = "%s"
890+
resource_instance_id = ibm_resource_instance.instance.id
891+
region_location = "%s"
892+
storage_class = "%s"
893+
retention_rule {
894+
default = %d
895+
maximum = %d
896+
minimum = %d
897+
permanent = false
898+
}
899+
}
900+
`, cosServiceName, bucketName, region, storageClass, default_retention, maximum_retention, minimum_retention)
901+
}

0 commit comments

Comments
 (0)