diff --git a/.secrets.baseline b/.secrets.baseline index b1c1965ec1..5b4fe2c05a 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -3,8 +3,11 @@ "files": "go.mod|go.sum|.*.map|^.secrets.baseline$", "lines": null }, - "generated_at": "2023-03-15T13:54:53Z", + "generated_at": "2023-03-17T12:44:55Z", "plugins_used": [ + { + "name": "AWSKeyDetector" + }, { "name": "ArtifactoryDetector" }, @@ -18,6 +21,12 @@ { "name": "BasicAuthDetector" }, + { + "name": "BoxDetector" + }, + { + "name": "CloudantDetector" + }, { "ghe_instance": "github.ibm.com", "name": "GheDetector" @@ -42,6 +51,9 @@ "keyword_exclude": null, "name": "KeywordDetector" }, + { + "name": "MailchimpDetector" + }, { "name": "NpmDetector" }, @@ -56,6 +68,12 @@ }, { "name": "SquareOAuthDetector" + }, + { + "name": "StripeDetector" + }, + { + "name": "TwilioKeyDetector" } ], "results": { @@ -784,7 +802,7 @@ "hashed_secret": "c8b6f5ef11b9223ac35a5663975a466ebe7ebba9", "is_secret": false, "is_verified": false, - "line_number": 1610, + "line_number": 1629, "type": "Secret Keyword", "verified_result": null }, @@ -792,7 +810,7 @@ "hashed_secret": "8abf4899c01104241510ba87685ad4de76b0c437", "is_secret": false, "is_verified": false, - "line_number": 1616, + "line_number": 1635, "type": "Secret Keyword", "verified_result": null } @@ -1798,7 +1816,7 @@ "hashed_secret": "884a58e4c2c5d195d3876787bdc63af6c5af2924", "is_secret": false, "is_verified": false, - "line_number": 448, + "line_number": 504, "type": "Secret Keyword", "verified_result": null } @@ -4746,7 +4764,7 @@ } ] }, - "version": "0.13.1+ibm.56.dss", + "version": "0.13.1+ibm.46.dss", "word_list": { "file": null, "hash": null diff --git a/examples/ibm-cos-bucket-object/README.md b/examples/ibm-cos-bucket-object/README.md index cd2d5393cd..2f7f8771f4 100644 --- a/examples/ibm-cos-bucket-object/README.md +++ b/examples/ibm-cos-bucket-object/README.md @@ -42,6 +42,27 @@ data "ibm_cos_bucket_object" "object" { } ``` +## COS objectlock + +**Note:** + + COS bucket must have objectlock enabled. + +ibm_cos_bucket_object objectlock: + +```hcl +resource "ibm_cos_bucket_object" "object" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + content = "Hello World 2" + key = "plaintext5.txt" + object_lock_mode = "COMPLIANCE" + object_lock_retain_until_date = "2023-02-15T18:00:00Z" + object_lock_legal_hold_status = "ON" + force_delete = true +} +``` + ## Requirements | Name | Version | @@ -66,6 +87,9 @@ data "ibm_cos_bucket_object" "object" { | endpoint\_type | The type of endpoint used to access COS. Valid options are "public", "private", and "direct". Defaults to "public". | `string` | false | | etag | MD5 hexdigest used to trigger updates. The only meaningful value is `filemd5("path/to/file")`. | `string` | false | | key | The name of the object in the COS bucket. | `string` | true | +| object_lock_legal_hold_status | An object lock configuration on the object, the valid states are ON/OFF. When ON prevents deletion of the object version. | `string` | false | +| object_lock_mode | Retention modes apply different levels of protection to the objects. | `string` | false | +| object_lock_legal_hold_status | An object cannot be deleted when the current time is earlier than the retainUntilDate. After this date, the object can be deleted. | `string` | false | ## Outputs diff --git a/examples/ibm-cos-bucket-object/main.tf b/examples/ibm-cos-bucket-object/main.tf index b971ccbcbf..63b12c2517 100644 --- a/examples/ibm-cos-bucket-object/main.tf +++ b/examples/ibm-cos-bucket-object/main.tf @@ -38,3 +38,27 @@ resource "ibm_cos_bucket_object" "file" { key = "file.json" etag = filemd5("${path.module}/helper/object.json") } + +// COS object lock + +resource "ibm_cos_bucket" "bucket_object_lock" { + bucket_name = var.bucket_name + resource_instance_id = ibm_resource_instance.cos_instance.id + cross_region_location = var.regional_loc + storage_class = var.standard_storage_class + object_versioning { + enable = true + } + object_lock = true +} + +resource "ibm_cos_bucket_object" "object_object_lock" { + bucket_crn = ibm_cos_bucket.object_lock.crn + bucket_location = ibm_cos_bucket.object_lock.cross_region_location + content = "Hello World 2" + key = "plaintext5.txt" + object_lock_mode = "COMPLIANCE" + object_lock_retain_until_date = "2023-02-15T18:00:00Z" + object_lock_legal_hold_status = "ON" + force_delete = true +} diff --git a/examples/ibm-cos-bucket/README.md b/examples/ibm-cos-bucket/README.md index 15b49148d9..e595bf28f3 100644 --- a/examples/ibm-cos-bucket/README.md +++ b/examples/ibm-cos-bucket/README.md @@ -353,7 +353,51 @@ resource "ibm_cos_bucket_replication_rule" "cos_bucket_repl" { } ``` +## COS Objectlock +COS objectlock feature enables user to store the object in a bucket with an extra layer of protection against object changes and deletion.Object Lock can help prevent objects from being deleted or overwritten for a fixed amount of time or indefinitely by setting up retention period and legalhold for an object. + +## Example usage +The following example creates an instance of IBM Cloud Object Storage.Then creates bucket with objectlock enabled and then set objectlock configuration on the bucket. + +```terraform +data "ibm_resource_group" "cos_group" { + name = "cos-resource-group" +} + +resource "ibm_resource_instance" "cos_instance" { + name = "cos-instance" + resource_group_id = data.ibm_resource_group.cos_group.id + service = "cloud-object-storage" + plan = "standard" + location = "global" +} + +resource "ibm_cos_bucket" "cos_bucket" { + bucket_name = "a-bucket" + resource_instance_id = ibm_resource_instance.cos_instance.id + region_location = "us-south" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true +} + +resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.cos_bucket.crn + bucket_location = ibm_cos_bucket.cos_bucket.region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "COMPLIANCE" + days = 4 + } + } + } +} +``` ## Requirements @@ -398,6 +442,7 @@ resource "ibm_cos_bucket_replication_rule" "cos_bucket_repl" { | permanent | Specifies a permanent retention status either enable or disable for a bucket. | `bool` | no | enable | Specifies Versioning status either **enable or suspended** for an objects in the bucket. | `bool` | no | hard_quota | sets a maximum amount of storage (in bytes) available for a bucket. | `int` | no +| object_lock | enables objectlock on a bucket. | `bool` | no | bucket\_crn | The CRN of the source COS bucket. | `string` | yes | | bucket\_location | The location of the source COS bucket. | `string` | yes | | destination_bucket_crn | The CRN of your destination bucket that you want to replicate to. | `String` | yes @@ -406,4 +451,11 @@ resource "ibm_cos_bucket_replication_rule" "cos_bucket_repl" { | rule_id | The rule id. | `String` | no | priority | A priority is associated with each rule. The rule will be applied in a higher priority if there are multiple rules configured. The higher the number, the higher the priority | `String` | no | prefix | An object key name prefix that identifies the subset of objects to which the rule applies. | `String` | no +| bucket_crn | The CRN of the COS bucket on which objectlock is enabled or should be enabled. | `String` | yes +| bucket_location | Location of the COS bucket. | `String` | yes +| endpoint_type | Endpoint types of the COS bucket. | `String` | no +| object_lock_enabled | Enable objectlock on an existing COS bucket. | `String` | yes +| mode | Retention mode for the objectlock configuration. | `String` | yes +| years | Retention period in terms of years after which the object can be deleted. | `int` | no +| days | Retention period in terms of days after which the object can be deleted. | `int` | no {: caption="inputs"} diff --git a/examples/ibm-cos-bucket/main.tf b/examples/ibm-cos-bucket/main.tf index 92b1f3fcf0..766c57fc2a 100644 --- a/examples/ibm-cos-bucket/main.tf +++ b/examples/ibm-cos-bucket/main.tf @@ -361,4 +361,38 @@ resource "ibm_cos_bucket" "cos_bucket_onerate" { region_location = var.regional_loc storage_class = var.onerate_storage_class } - \ No newline at end of file + +#COS objectlock + +resource "ibm_resource_instance" "cos_instance2" { + name = "cos-instance" + resource_group_id = data.ibm_resource_group.cos_group.id + service = "cloud-object-storage" + plan = "standard" + location = "global" +} + +resource "ibm_cos_bucket" "bucket" { + bucket_name = var.bucket_name + resource_instance_id = ibm_resource_instance.cos_instance2.id + region_location = var.regional_loc + storage_class = var.standard_storage_class + object_versioning { + enable = true + } + object_lock = true +} + +resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = var.regional_loc + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "COMPLIANCE" + days = 6 + } + } + } +} diff --git a/examples/ibm-cos-bucket/provider.tf b/examples/ibm-cos-bucket/provider.tf index 8a9ca7d17c..0559ccf4c8 100644 --- a/examples/ibm-cos-bucket/provider.tf +++ b/examples/ibm-cos-bucket/provider.tf @@ -14,4 +14,5 @@ provider "ibm" { iaas_classic_username = var.iaas_classic_username iaas_classic_api_key = var.iaas_classic_api_key ibmcloud_api_key = var.ibmcloud_api_key + } diff --git a/go.mod b/go.mod index bac48d7cf1..2de02db821 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/IBM/event-notifications-go-admin-sdk v0.1.7 github.com/IBM/eventstreams-go-sdk v1.2.0 github.com/IBM/go-sdk-core/v5 v5.12.1 - github.com/IBM/ibm-cos-sdk-go v1.9.0 + github.com/IBM/ibm-cos-sdk-go v1.10.0 github.com/IBM/ibm-cos-sdk-go-config v1.2.0 github.com/IBM/ibm-hpcs-tke-sdk v0.0.0-20211109141421-a4b61b05f7d1 github.com/IBM/ibm-hpcs-uko-sdk v0.0.4 @@ -150,11 +150,11 @@ require ( github.com/vmihailenco/msgpack/v4 v4.3.12 // indirect github.com/vmihailenco/tagparser v0.1.1 // indirect github.com/zclconf/go-cty v1.11.0 // indirect - golang.org/x/net v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/term v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/sys v0.6.0 // indirect + golang.org/x/term v0.6.0 // indirect + golang.org/x/text v0.8.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154 // indirect diff --git a/go.sum b/go.sum index 007d63940a..38ff52e289 100644 --- a/go.sum +++ b/go.sum @@ -81,6 +81,8 @@ github.com/IBM/go-sdk-core/v5 v5.12.1/go.mod h1:WZPFasUzsKab/2mzt29xPcfruSk5js2y github.com/IBM/ibm-cos-sdk-go v1.3.1/go.mod h1:YLBAYobEA8bD27P7xpMwSQeNQu6W3DNBtBComXrRzRY= github.com/IBM/ibm-cos-sdk-go v1.9.0 h1:kXTLB9GBwks3+YZopYz/eRbdyeVl2BXFALeqtQ8Duoc= github.com/IBM/ibm-cos-sdk-go v1.9.0/go.mod h1:Oi8AC5WNDhmUJgbo1GL2FtBdo0nRgbzE/1HmCL1SERU= +github.com/IBM/ibm-cos-sdk-go v1.10.0 h1:/2VIev2/jBei39OqU2+nSZQnoWJ+KtkiSAIDkqsd7uU= +github.com/IBM/ibm-cos-sdk-go v1.10.0/go.mod h1:C8KRTRaoD3CWPPBOa6FCOpdh0ZMlUjKAAA4i3F+Q/sc= github.com/IBM/ibm-cos-sdk-go-config v1.2.0 h1:1E93234yZgVS0ntm7eUwVb3h0AAayPGcxEhhizEN1LE= github.com/IBM/ibm-cos-sdk-go-config v1.2.0/go.mod h1:Wetfgv6m1xyuzpZLQTTLIBsWstxjYa15h+Utj7x53Dk= github.com/IBM/ibm-hpcs-tke-sdk v0.0.0-20211109141421-a4b61b05f7d1 h1:T5UwRKKd+BoaPZ7UIlpJrzXzVTUEs8HcxwQ3pCIbORs= @@ -725,6 +727,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -735,6 +738,7 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= @@ -848,6 +852,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -904,8 +909,11 @@ golang.org/x/net v0.0.0-20220812174116-3211cb980234/go.mod h1:YDH+HFinaLZZlnHAfS golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -925,6 +933,7 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -997,12 +1006,16 @@ golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI= golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1016,6 +1029,8 @@ golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM= golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1076,6 +1091,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ibm/flex/structures.go b/ibm/flex/structures.go index 3a28ba8d3a..909c119275 100644 --- a/ibm/flex/structures.go +++ b/ibm/flex/structures.go @@ -26,6 +26,7 @@ import ( "github.com/IBM/cloud-databases-go-sdk/clouddatabasesv5" "github.com/IBM/go-sdk-core/v5/core" "github.com/IBM/ibm-cos-sdk-go-config/resourceconfigurationv1" + "github.com/IBM/ibm-cos-sdk-go/aws" "github.com/IBM/ibm-cos-sdk-go/service/s3" kp "github.com/IBM/keyprotect-go-client" "github.com/IBM/platform-services-go-sdk/globalsearchv2" @@ -892,6 +893,58 @@ func ReplicationRuleGet(in *s3.ReplicationConfiguration) []map[string]interface{ return rules } +func ObjectLockConfigurationGet(in *s3.ObjectLockConfiguration) []map[string]interface{} { + configuration := make([]map[string]interface{}, 0, 1) + if in != nil { + objectLockConfig := make(map[string]interface{}) + + if in.ObjectLockEnabled != nil { + objectLockConfig["object_lock_enabled"] = s3.ObjectLockEnabledEnabled + } + if in.Rule != nil { + objectLockConfig["object_lock_rule"] = ObjectLockRuleGet(in.Rule) + } + + configuration = append(configuration, objectLockConfig) + } + return configuration +} +func ObjectLockRuleGet(in *s3.ObjectLockRule) []map[string]interface{} { + objectLockRule := make([]map[string]interface{}, 0, 1) + if in != nil { + objectLockConfig := make(map[string]interface{}) + + if in.DefaultRetention != nil { + objectLockConfig["default_retention"] = ObjectLockDefaultRetentionGet(in.DefaultRetention) + } + + objectLockRule = append(objectLockRule, objectLockConfig) + } + return objectLockRule +} + +func ObjectLockDefaultRetentionGet(in *s3.DefaultRetention) []map[string]interface{} { + defaultRetention := make([]map[string]interface{}, 0, 1) + if in != nil { + defaultRetentionMap := make(map[string]interface{}) + + if in.Days != nil { + defaultRetentionMap["days"] = int(aws.Int64Value(in.Days)) + } + + if in.Mode != nil { + defaultRetentionMap["mode"] = aws.StringValue(in.Mode) + } + + if in.Years != nil { + defaultRetentionMap["years"] = int(aws.Int64Value(in.Years)) + } + + defaultRetention = append(defaultRetention, defaultRetentionMap) + + } + return defaultRetention +} func FlattenLimits(in *whisk.Limits) []interface{} { att := make(map[string]interface{}) if in.Timeout != nil { diff --git a/ibm/provider/provider.go b/ibm/provider/provider.go index b30918ca90..3915d1a78a 100644 --- a/ibm/provider/provider.go +++ b/ibm/provider/provider.go @@ -893,6 +893,7 @@ func Provider() *schema.Provider { "ibm_cos_bucket": cos.ResourceIBMCOSBucket(), "ibm_cos_bucket_replication_rule": cos.ResourceIBMCOSBucketReplicationConfiguration(), "ibm_cos_bucket_object": cos.ResourceIBMCOSBucketObject(), + "ibm_cos_bucket_object_lock_configuration": cos.ResourceIBMCOSBucketObjectlock(), "ibm_dns_domain": classicinfrastructure.ResourceIBMDNSDomain(), "ibm_dns_domain_registration_nameservers": classicinfrastructure.ResourceIBMDNSDomainRegistrationNameservers(), "ibm_dns_secondary": classicinfrastructure.ResourceIBMDNSSecondary(), diff --git a/ibm/service/cos/data_source_ibm_cos_bucket.go b/ibm/service/cos/data_source_ibm_cos_bucket.go index c271f38708..3f4d28caf9 100644 --- a/ibm/service/cos/data_source_ibm_cos_bucket.go +++ b/ibm/service/cos/data_source_ibm_cos_bucket.go @@ -365,6 +365,62 @@ func DataSourceIBMCosBucket() *schema.Resource { Computed: true, Description: "sets a maximum amount of storage (in bytes) available for a bucket", }, + "object_lock": { + Type: schema.TypeBool, + Computed: true, + Description: "Description", + }, + "object_lock_configuration": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "Bucket level object lock settings includes Days, Years, Mode.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "object_lock_enabled": { + Type: schema.TypeString, + Required: true, + Description: "Enable object lock on a COS bucket. This can be used to enable objectlock on an existing bucket", + }, + "object_lock_rule": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_retention": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "An object lock configuration on the object at a bucket level, in the form of a days , years and mode that establishes a point in time after which the object can be deleted. This is applied at bucket level hence it is by default applied to all the object in the bucket unless a seperate retention period is set on the object.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Required: true, + Description: "Retention modes apply different levels of protection to the objects.", + }, + "years": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"object_lock_configuration.0.object_lock_rule.0.default_retention.0.days"}, + Description: "Retention period in terms of years after which the object can be deleted.", + }, + "days": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"object_lock_configuration.0.object_lock_rule.0.default_retention.0.years"}, + Description: "Retention period in terms of days after which the object can be deleted.", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, }, } } @@ -638,6 +694,22 @@ func dataSourceIBMCosBucketRead(d *schema.ResourceData, meta interface{}) error d.Set("replication_rule", replicationRules) } } + // reading objectlock for bucket + getObjectLockConfigurationInput := &s3.GetObjectLockConfigurationInput{ + Bucket: aws.String(bucketName), + } + output, err := s3Client.GetObjectLockConfiguration(getObjectLockConfigurationInput) + if output.ObjectLockConfiguration != nil { + objectLockEnabled := *output.ObjectLockConfiguration.ObjectLockEnabled + if objectLockEnabled == "Enabled" { + d.Set("object_lock", true) + } + objectLockConfigurationptr := output.ObjectLockConfiguration + objectLockConfiguration := flex.ObjectLockConfigurationGet(objectLockConfigurationptr) + if len(objectLockConfiguration) > 0 { + d.Set("object_lock_configuration", objectLockConfiguration) + } + } return nil } diff --git a/ibm/service/cos/data_source_ibm_cos_bucket_object.go b/ibm/service/cos/data_source_ibm_cos_bucket_object.go index 9fc057e887..fccc497766 100644 --- a/ibm/service/cos/data_source_ibm_cos_bucket_object.go +++ b/ibm/service/cos/data_source_ibm_cos_bucket_object.go @@ -17,6 +17,7 @@ import ( "github.com/IBM/ibm-cos-sdk-go/service/s3" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + validation "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func DataSourceIBMCosBucketObject() *schema.Resource { @@ -79,6 +80,21 @@ func DataSourceIBMCosBucketObject() *schema.Resource { Type: schema.TypeString, Computed: true, }, + "object_lock_legal_hold_status": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(s3.ObjectLockLegalHoldStatus_Values(), false), + }, + "object_lock_mode": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(s3.ObjectLockMode_Values(), false), + }, + "object_lock_retain_until_date": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.IsRFC3339Time, + }, }, } } @@ -150,6 +166,32 @@ func dataSourceIBMCosBucketObjectRead(ctx context.Context, d *schema.ResourceDat log.Printf("[INFO] Ignoring body of COS bucket (%s) object (%s) with Content-Type %q", bucketName, objectKey, contentType) } + getObjectRetentionInput := new(s3.GetObjectRetentionInput) + getObjectRetentionInput.Bucket = aws.String(bucketName) + getObjectRetentionInput.Key = aws.String(objectKey) + response, err := s3Client.GetObjectRetention(getObjectRetentionInput) + if err != nil { + return diag.FromErr(fmt.Errorf("failed getting COS bucket (%s) object retention (%s): %w", bucketName, objectKey, err)) + } + objectretentionptr := response.Retention + if objectretentionptr != nil { + d.Set("object_lock_mode", *objectretentionptr.Mode) + retainuntildatestring := objectDatetoString(objectretentionptr.RetainUntilDate) + d.Set("object_lock_retain_until_date", retainuntildatestring) + + } + getObjectLegalHoldInput := new(s3.GetObjectLegalHoldInput) + getObjectLegalHoldInput.Bucket = aws.String(bucketName) + getObjectLegalHoldInput.Key = aws.String(objectKey) + response1, err := s3Client.GetObjectLegalHold(getObjectLegalHoldInput) + if err != nil { + return diag.FromErr(fmt.Errorf("failed getting COS bucket (%s) object retention (%s): %w", bucketName, objectKey, err)) + } + objectlegalholdptr := response1.LegalHold + if objectlegalholdptr != nil { + d.Set("object_lock_legal_hold_status", *objectlegalholdptr.Status) + + } objectID := getObjectId(bucketCRN, objectKey, bucketLocation) d.SetId(objectID) diff --git a/ibm/service/cos/resource_ibm_cos_bucket.go b/ibm/service/cos/resource_ibm_cos_bucket.go index e43aa2c3b4..3986e84541 100644 --- a/ibm/service/cos/resource_ibm_cos_bucket.go +++ b/ibm/service/cos/resource_ibm_cos_bucket.go @@ -435,6 +435,12 @@ func ResourceIBMCOSBucket() *schema.Resource { Default: true, Description: "COS buckets need to be empty before they can be deleted. force_delete option empty the bucket and delete it.", }, + "object_lock": { + Type: schema.TypeBool, + Optional: true, + RequiredWith: []string{"object_versioning"}, + Description: "Enable objectlock for the bucket. When enabled, buckets within the container vault can have Object Lock Configuration applied to the bucket.", + }, }, } } @@ -1240,6 +1246,17 @@ func resourceIBMCOSBucketRead(d *schema.ResourceData, meta interface{}) error { d.Set("object_versioning", nil) } } + // reading objectlock + getObjectLockConfigurationInput := &s3.GetObjectLockConfigurationInput{ + Bucket: aws.String(bucketName), + } + output, err := s3Client.GetObjectLockConfiguration(getObjectLockConfigurationInput) + if output.ObjectLockConfiguration != nil { + objectLockEnabled := *output.ObjectLockConfiguration.ObjectLockEnabled + if objectLockEnabled == "Enabled" { + d.Set("object_lock", true) + } + } return nil } @@ -1251,6 +1268,7 @@ func resourceIBMCOSBucketCreate(d *schema.ResourceData, meta interface{}) error } bucketName := d.Get("bucket_name").(string) storageClass := d.Get("storage_class").(string) + objectLockEnabled := d.Get("object_lock").(bool) var bLocation string var apiType string var satlc_id string @@ -1316,6 +1334,11 @@ func resourceIBMCOSBucketCreate(d *schema.ResourceData, meta interface{}) error create = &s3.CreateBucketInput{ Bucket: aws.String(bucketName), } + } else if objectLockEnabled == true { + create = &s3.CreateBucketInput{ + Bucket: aws.String(bucketName), + ObjectLockEnabledForBucket: aws.Bool(true), + } } else { create = &s3.CreateBucketInput{ Bucket: aws.String(bucketName), diff --git a/ibm/service/cos/resource_ibm_cos_bucket_object.go b/ibm/service/cos/resource_ibm_cos_bucket_object.go index 048a0b3e04..843bd4cab4 100644 --- a/ibm/service/cos/resource_ibm_cos_bucket_object.go +++ b/ibm/service/cos/resource_ibm_cos_bucket_object.go @@ -26,6 +26,7 @@ import ( "github.com/IBM/ibm-cos-sdk-go/service/s3" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + validation "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" ) func ResourceIBMCOSBucketObject() *schema.Resource { @@ -126,6 +127,25 @@ func ResourceIBMCOSBucketObject() *schema.Resource { Computed: true, Description: "Access the object using an SQL Query instance.The reference url is used to perform queries against objects storing structured data.", }, + "object_lock_legal_hold_status": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validation.StringInSlice(s3.ObjectLockLegalHoldStatus_Values(), false), + Description: "An object lock configuration on the object, the valid states are ON/OFF. When ON prevents deletion of the object version.", + }, + "object_lock_mode": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"object_lock_retain_until_date"}, + Description: "Retention modes apply different levels of protection to the objects.", + }, + "object_lock_retain_until_date": { + Type: schema.TypeString, + Optional: true, + RequiredWith: []string{"object_lock_mode"}, + ValidateFunc: validation.IsRFC3339Time, + Description: "An object cannot be deleted when the current time is earlier than the retainUntilDate. After this date, the object can be deleted.", + }, }, } } @@ -187,7 +207,38 @@ func resourceIBMCOSBucketObjectCreate(ctx context.Context, d *schema.ResourceDat if _, err := s3Client.PutObject(putInput); err != nil { return diag.FromErr(fmt.Errorf("[ERROR] Error putting object (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) } + if v, ok := d.GetOk("object_lock_mode"); ok { + if d, ok := d.GetOk("object_lock_retain_until_date"); ok { + retainUntildate := parseDate(d.(string)) + putObjectRetentionInput := &s3.PutObjectRetentionInput{ + Bucket: aws.String(bucketName), + Key: aws.String(objectKey), + Retention: &s3.ObjectLockRetention{ + Mode: aws.String(v.(string)), + RetainUntilDate: aws.Time(*retainUntildate), + }, + } + _, err = s3Client.PutObjectRetention(putObjectRetentionInput) + if err != nil { + return diag.FromErr(fmt.Errorf("[ERROR] Error putting objectlock retention on (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) + } + + } + } + if v, ok := d.GetOk("object_lock_legal_hold_status"); ok { + putObjectLegalHoldInput := &s3.PutObjectLegalHoldInput{ + Bucket: aws.String(bucketName), + Key: aws.String(objectKey), + LegalHold: &s3.ObjectLockLegalHold{ + Status: aws.String(v.(string)), + }, + } + _, err = s3Client.PutObjectLegalHold(putObjectLegalHoldInput) + if err != nil { + return diag.FromErr(fmt.Errorf("[ERROR] Error putting objectlock legalhold on (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) + } + } objectID := getObjectId(bucketCRN, objectKey, bucketLocation) d.SetId(objectID) @@ -269,6 +320,35 @@ func resourceIBMCOSBucketObjectRead(ctx context.Context, d *schema.ResourceData, log.Printf("[INFO] Ignoring body of COS bucket (%s) object (%s) with Content-Type %q", bucketName, objectKey, contentType) } + getObjectRetentionInput := new(s3.GetObjectRetentionInput) + getObjectRetentionInput.Bucket = aws.String(bucketName) + getObjectRetentionInput.Key = aws.String(objectKey) + response, err := s3Client.GetObjectRetention(getObjectRetentionInput) + if err != nil { + return diag.FromErr(fmt.Errorf("Failed to get objectlock retention for the object (%s) : %w", objectKey, err)) + } + if response.Retention != nil { + objectretentionptr := response.Retention + + d.Set("object_lock_mode", *objectretentionptr.Mode) + retainuntildatestring := objectDatetoString(objectretentionptr.RetainUntilDate) + d.Set("object_lock_retain_until_date", retainuntildatestring) + + } + getObjectLegalHoldInput := new(s3.GetObjectLegalHoldInput) + getObjectLegalHoldInput.Bucket = aws.String(bucketName) + getObjectLegalHoldInput.Key = aws.String(objectKey) + response1, err := s3Client.GetObjectLegalHold(getObjectLegalHoldInput) + if err != nil { + return diag.FromErr(fmt.Errorf("Failed to get objectlock legalhold for the object (%s) : %w", objectKey, err)) + + } + if response1.LegalHold != nil { + objectlegalholdptr := response1.LegalHold + + d.Set("object_lock_legal_hold_status", *objectlegalholdptr.Status) + + } d.Set("key", objectKey) d.Set("version_id", out.VersionId) d.Set("object_sql_url", "cos://"+bucketLocation+"/"+bucketName+"/"+objectKey) @@ -276,23 +356,22 @@ func resourceIBMCOSBucketObjectRead(ctx context.Context, d *schema.ResourceData, } func resourceIBMCOSBucketObjectUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) diag.Diagnostics { - if d.HasChanges("content", "content_base64", "content_file", "etag") { - bucketCRN := d.Get("bucket_crn").(string) - bucketName := strings.Split(bucketCRN, ":bucket:")[1] - instanceCRN := fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) - - bucketLocation := d.Get("bucket_location").(string) - endpointType := d.Get("endpoint_type").(string) - - bxSession, err := m.(conns.ClientSession).BluemixSession() - if err != nil { - return diag.FromErr(err) - } + bucketCRN := d.Get("bucket_crn").(string) + bucketName := strings.Split(bucketCRN, ":bucket:")[1] + instanceCRN := fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + objectKey := d.Get("key").(string) + bucketLocation := d.Get("bucket_location").(string) + endpointType := d.Get("endpoint_type").(string) + bxSession, err := m.(conns.ClientSession).BluemixSession() + if err != nil { + return diag.FromErr(err) + } - s3Client, err := getS3Client(bxSession, bucketLocation, endpointType, instanceCRN) - if err != nil { - return diag.FromErr(err) - } + s3Client, err := getS3Client(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return diag.FromErr(err) + } + if d.HasChanges("content", "content_base64", "content_file", "etag") { var body io.ReadSeeker @@ -334,10 +413,39 @@ func resourceIBMCOSBucketObjectUpdate(ctx context.Context, d *schema.ResourceDat return diag.FromErr(fmt.Errorf("[ERROR] Error putting object (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) } - objectID := getObjectId(bucketCRN, objectKey, bucketLocation) - d.SetId(objectID) + } + if d.HasChange("object_lock_legal_hold_status") { + putObjectLegalHoldInput := &s3.PutObjectLegalHoldInput{ + Bucket: aws.String(bucketName), + Key: aws.String(objectKey), + LegalHold: &s3.ObjectLockLegalHold{ + Status: aws.String((d.Get("object_lock_legal_hold_status").(string))), + }, + } + _, err = s3Client.PutObjectLegalHold(putObjectLegalHoldInput) + if err != nil { + return diag.FromErr(fmt.Errorf("[ERROR] Error putting objectlock on legalhold (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) + } + } + + if d.HasChanges("object_lock_mode", "object_lock_retain_until_date") { + putObjectRetentionInput := &s3.PutObjectRetentionInput{ + Bucket: aws.String(bucketName), + Key: aws.String(objectKey), + Retention: &s3.ObjectLockRetention{ + Mode: aws.String(d.Get("object_lock_mode").(string)), + RetainUntilDate: parseDate(d.Get("object_lock_retain_until_date").(string)), + }, + } + _, err = s3Client.PutObjectRetention(putObjectRetentionInput) + if err != nil { + return diag.FromErr(fmt.Errorf("[ERROR] Error putting objectlock retention on (%s) in COS bucket (%s): %s", objectKey, bucketName, err)) + } } + objectID := getObjectId(bucketCRN, objectKey, bucketLocation) + d.SetId(objectID) + return resourceIBMCOSBucketObjectRead(ctx, d, m) } @@ -615,3 +723,18 @@ func deleteCOSObjectVersion(conn *s3.S3, b, k, v string, force bool) error { return err } +func parseDate(v string) *time.Time { + t, err := time.Parse(time.RFC3339, v) + if err != nil { + return nil + } + + return aws.Time(t) +} + +func objectDatetoString(t *time.Time) string { + if t == nil { + return "" + } + return t.Format(time.RFC3339) +} diff --git a/ibm/service/cos/resource_ibm_cos_bucket_object_test.go b/ibm/service/cos/resource_ibm_cos_bucket_object_test.go index f2aff34123..648ecb8213 100644 --- a/ibm/service/cos/resource_ibm_cos_bucket_object_test.go +++ b/ibm/service/cos/resource_ibm_cos_bucket_object_test.go @@ -7,7 +7,9 @@ import ( "encoding/base64" "fmt" "io/ioutil" + "regexp" "testing" + "time" acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" @@ -165,6 +167,112 @@ func TestAccIBMCOSBucketObject_Uploading_Multile_Objects_on_Existing_Bucket_with }) } +func TestAccIBMCOSBucketObjectlock_Retention_Without_Mode(t *testing.T) { + name := fmt.Sprintf("tf-testacc-cos-%d", acctest.RandIntRange(10, 100)) + instanceCRN := acc.CosCRN + objectBody := "Acceptance Testing" + retainUntilDate := time.Now().Local().Add(time.Second * 5) + retainUntilDateString := retainUntilDate.Format(time.RFC3339) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheckCOS(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIBMCOSBucketObjectlock_retention_without_mode(name, instanceCRN, objectBody, retainUntilDateString), + ExpectError: regexp.MustCompile("MalformedXML: The XML you provided was not well-formed or did not validate against our published schema."), + }, + }, + }) +} + +func TestAccIBMCOSBucketObjectlock_retention_without_objectlock_enabled(t *testing.T) { + name := fmt.Sprintf("tf-testacc-cos-%d", acctest.RandIntRange(10, 100)) + instanceCRN := acc.CosCRN + mode := "COMLIANCE" + retainUntilDate := time.Now().Local().Add(time.Second * 22) + retainUntilDateString := retainUntilDate.Format(time.RFC3339) + objectBody := "Acceptance Testing" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheckCOS(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIBMCOSBucketObjectlock_retention_without_objectlock_enabled(name, instanceCRN, objectBody, mode, retainUntilDateString), + ExpectError: regexp.MustCompile("InvalidRequest: Bucket is missing Object Lock Configuration"), + }, + }, + }) +} + +func TestAccIBMCOSBucketObjectlock_legalhold_without_objectlock_enabled(t *testing.T) { + name := fmt.Sprintf("tf-testacc-cos-%d", acctest.RandIntRange(10, 100)) + instanceCRN := acc.CosCRN + objectBody := "Acceptance Testing" + legalhold := "ON" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheckCOS(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIBMCOSBucketObjectlock_legalhold_without_objectlock_enabled(name, instanceCRN, objectBody, legalhold), + ExpectError: regexp.MustCompile("InvalidRequest: Bucket is missing Object Lock Configuration"), + }, + }, + }) +} +func TestAccIBMCOSBucketObjectlock_Retention_Invalid_Mode(t *testing.T) { + name := fmt.Sprintf("tf-testacc-cos-%d", acctest.RandIntRange(10, 100)) + instanceCRN := acc.CosCRN + mode := "INVALID" + objectBody := "Acceptance Testing" + retainUntilDate := time.Now().Local().Add(time.Second * 5) + retainUntilDateString := retainUntilDate.Format(time.RFC3339) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheckCOS(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIBMCOSBucketObjectlock_retention_invalid_mode(name, instanceCRN, objectBody, mode, retainUntilDateString), + ExpectError: regexp.MustCompile("MalformedXML: The XML you provided was not well-formed or did not validate against our published schema."), + }, + }, + }) +} + +func TestAccIBMCOSBucketObjectlock_Retention_Retainuntildate_Past(t *testing.T) { + name := fmt.Sprintf("tf-testacc-cos-%d", acctest.RandIntRange(10, 100)) + instanceCRN := acc.CosCRN + objectBody := "Acceptance Testing" + retainUntilDateString := "2020-02-15T00:00:00Z" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheckCOS(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIBMCOSBucketObjectlock_Retention_Retainuntildate_Past(name, instanceCRN, objectBody, retainUntilDateString), + ExpectError: regexp.MustCompile("InvalidArgument: The retain until date must be in the future!"), + }, + }, + }) +} + +func TestAccIBMCOSBucketObjectlock_Retention_Without_Retainuntildate(t *testing.T) { + name := fmt.Sprintf("tf-testacc-cos-%d", acctest.RandIntRange(10, 100)) + instanceCRN := acc.CosCRN + objectBody := "Acceptance Testing" + mode := "COMPLIANCE" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheckCOS(t) }, + Providers: acc.TestAccProviders, + Steps: []resource.TestStep{ + { + Config: testAccIBMCOSBucketObjectlock_Retention_Without_Retainuntildate(name, instanceCRN, objectBody, mode), + ExpectError: regexp.MustCompile("MalformedXML: The XML you provided was not well-formed or did not validate against our published schema."), + }, + }, + }) +} + func testAccIBMCOSBucketObjectConfig_plaintext(name string, instanceCRN string, objectBody string) string { return fmt.Sprintf(` resource "ibm_cos_bucket" "testacc" { @@ -247,3 +355,156 @@ func testAccIBMCOSBucketBucketObjectUpload(bucketCrn string, key string, objectB content = "%[3]s" }`, bucketCrn, key, objectBody1) } + +func testAccIBMCOSBucketObjectlock_retention_without_objectlock_enabled(name string, instanceCRN string, objectBody string, mode string, retainUntilDate string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_mode = "%[4]s" + object_lock_retain_until_date = "%[5]s" + force_delete = true + }`, name, instanceCRN, objectBody, mode, retainUntilDate) +} + +func testAccIBMCOSBucketObjectlock_legalhold_without_objectlock_enabled(name string, instanceCRN string, objectBody string, legalhold string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_legal_hold_status = "%[4]s" + force_delete = true + }`, name, instanceCRN, objectBody, legalhold) +} +func testAccIBMCOSBucketObjectlock_retention_without_mode(name string, instanceCRN string, objectBody string, retainUntilDate string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_retain_until_date = "%[4]s" + force_delete = true + }`, name, instanceCRN, objectBody, retainUntilDate) +} + +func testAccIBMCOSBucketObjectlock_retention_invalid_mode(name string, instanceCRN string, objectBody string, mode string, retainUntilDate string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_mode = "%[4]s" + object_lock_retain_until_date = "%[5]s" + force_delete = true + }`, name, instanceCRN, objectBody, mode, retainUntilDate) +} + +func testAccIBMCOSBucketObjectlock_Retention_Retainuntildate_Past(name string, instanceCRN string, objectBody string, retainUntilDate string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_mode = "COMPLIANCE" + object_lock_retain_until_date = "%[4]s" + force_delete = true + }`, name, instanceCRN, objectBody, retainUntilDate) +} + +func testAccIBMCOSBucketObjectlock_Retention_Without_Retainuntildate(name string, instanceCRN string, objectBody string, mode string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_mode = "%[4]s" + force_delete = true + }`, name, instanceCRN, objectBody, mode) +} +func testAccIBMCOSBucketObjectlock_legalhold_off(name string, instanceCRN string, objectBody string) string { + return fmt.Sprintf(` + resource "ibm_cos_bucket" "testacc" { + bucket_name = "%[1]s" + resource_instance_id = "%[2]s" + cross_region_location = "us" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true + } + resource "ibm_cos_bucket_object" "testacc" { + bucket_crn = ibm_cos_bucket.testacc.crn + bucket_location = ibm_cos_bucket.testacc.cross_region_location + key = "%[1]s.txt" + content = "%[3]s" + object_lock_legal_hold_status = "OFF" + force_delete = true + }`, name, instanceCRN, objectBody) +} diff --git a/ibm/service/cos/resource_ibm_cos_bucket_objectlock_configuration.go b/ibm/service/cos/resource_ibm_cos_bucket_objectlock_configuration.go new file mode 100644 index 0000000000..c58befc5b5 --- /dev/null +++ b/ibm/service/cos/resource_ibm_cos_bucket_objectlock_configuration.go @@ -0,0 +1,348 @@ +package cos + +import ( + "fmt" + "strings" + "time" + + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/conns" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/flex" + "github.com/IBM-Cloud/terraform-provider-ibm/ibm/validate" + "github.com/IBM/ibm-cos-sdk-go/aws" + "github.com/IBM/ibm-cos-sdk-go/service/s3" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func ResourceIBMCOSBucketObjectlock() *schema.Resource { + return &schema.Resource{ + Create: resourceIBMCOSBucketObjectlockCreate, + Read: resourceIBMCOSBucketObjectlockRead, + Update: resourceIBMCOSBucketObjectlockUpdate, + Delete: resourceIBMCOSBucketObjectlockDelete, + Importer: &schema.ResourceImporter{}, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(60 * time.Minute), + Update: schema.DefaultTimeout(20 * time.Minute), + Delete: schema.DefaultTimeout(10 * time.Minute), + }, + Schema: map[string]*schema.Schema{ + "bucket_crn": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "COS bucket CRN", + }, + "bucket_location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "COS bucket location", + }, + "endpoint_type": { + Type: schema.TypeString, + Optional: true, + ValidateFunc: validate.ValidateAllowedStringValues([]string{"public", "private", "direct"}), + Description: "COS endpoint type: public, private, direct", + Default: "public", + }, + "object_lock_configuration": { + Type: schema.TypeList, + Required: true, + MaxItems: 1, + Description: "Bucket level object lock settings includes Days, Years, Mode.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "object_lock_enabled": { + Type: schema.TypeString, + Required: true, + Description: "Enable object lock on a COS bucket. This can be used to enable objectlock on an existing bucket", + }, + "object_lock_rule": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "default_retention": { + Type: schema.TypeList, + Optional: true, + MaxItems: 1, + Description: "An object lock configuration on the object at a bucket level, in the form of a days , years and mode that establishes a point in time after which the object can be deleted. This is applied at bucket level hence it is by default applied to all the object in the bucket unless a seperate retention period is set on the object.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "mode": { + Type: schema.TypeString, + Required: true, + Description: "Retention modes apply different levels of protection to the objects.", + }, + "years": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"object_lock_configuration.0.object_lock_rule.0.default_retention.0.days"}, + Description: "Retention period in terms of years after which the object can be deleted.", + }, + "days": { + Type: schema.TypeInt, + Optional: true, + ConflictsWith: []string{"object_lock_configuration.0.object_lock_rule.0.default_retention.0.years"}, + Description: "Retention period in terms of days after which the object can be deleted.", + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + }, + } +} + +func resourceIBMCOSBucketObjectlockCreate(d *schema.ResourceData, meta interface{}) error { + + bucketCRN := d.Get("bucket_crn").(string) + bucketName := strings.Split(bucketCRN, ":bucket:")[1] + instanceCRN := fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + + bucketLocation := d.Get("bucket_location").(string) + endpointType := d.Get("endpoint_type").(string) + + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + var objectLockConfiguration *s3.ObjectLockConfiguration + configuration, ok := d.GetOk("object_lock_configuration") + if ok { + objectLockConfiguration = objectLockConfigurationSet(configuration.([]interface{})) + + } + putObjectLockConfigurationInput := &s3.PutObjectLockConfigurationInput{ + Bucket: aws.String(bucketName), + ObjectLockConfiguration: objectLockConfiguration, + } + _, err = s3Client.PutObjectLockConfiguration(putObjectLockConfigurationInput) + + if err != nil { + return fmt.Errorf("failed to put objectlock configuration on the COS bucket %s, %v", bucketName, err) + } + bktID := fmt.Sprintf("%s:%s:%s:meta:%s:%s", strings.Replace(instanceCRN, "::", "", -1), "bucket", bucketName, bucketLocation, endpointType) + d.SetId(bktID) + return resourceIBMCOSBucketObjectlockUpdate(d, meta) +} + +func resourceIBMCOSBucketObjectlockUpdate(d *schema.ResourceData, meta interface{}) error { + bucketCRN := d.Get("bucket_crn").(string) + bucketName := strings.Split(bucketCRN, ":bucket:")[1] + instanceCRN := fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + + bucketLocation := d.Get("bucket_location").(string) + endpointType := d.Get("endpoint_type").(string) + + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return err + } + if d.HasChange("object_lock_configuration") { + var objectLockConfiguration *s3.ObjectLockConfiguration + configuration, ok := d.GetOk("object_lock_configuration") + if ok { + objectLockConfiguration = objectLockConfigurationSet(configuration.([]interface{})) + } + putObjectLockConfigurationInput := &s3.PutObjectLockConfigurationInput{ + Bucket: aws.String(bucketName), + ObjectLockConfiguration: objectLockConfiguration, + } + _, err = s3Client.PutObjectLockConfiguration(putObjectLockConfigurationInput) + + if err != nil { + return fmt.Errorf("failed to update objectlock configuration on the COS bucket %s, %v", bucketName, err) + } + } + return resourceIBMCOSBucketObjectlockRead(d, meta) +} + +func resourceIBMCOSBucketObjectlockRead(d *schema.ResourceData, meta interface{}) error { + bucketCRN := parseObjectLockId(d.Id(), "bucketCRN") + bucketName := parseObjectLockId(d.Id(), "bucketName") + bucketLocation := parseObjectLockId(d.Id(), "bucketLocation") + instanceCRN := parseObjectLockId(d.Id(), "instanceCRN") + endpointType := parseObjectLockId(d.Id(), "endpointType") + + d.Set("bucket_crn", bucketCRN) + d.Set("bucket_location", bucketLocation) + if endpointType != "" { + d.Set("endpoint_type", endpointType) + } + + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return err + } + //object lock configuration + getObjectLockConfigurationInput := &s3.GetObjectLockConfigurationInput{ + Bucket: aws.String(bucketName), + } + + output, err := s3Client.GetObjectLockConfiguration(getObjectLockConfigurationInput) + + if err != nil && !strings.Contains(err.Error(), "AccessDenied: Access Denied") { + return err + } + if output.ObjectLockConfiguration != nil { + objectLockConfigurationptr := output.ObjectLockConfiguration + + objectLockConfiguration := flex.ObjectLockConfigurationGet(objectLockConfigurationptr) + if len(objectLockConfiguration) > 0 { + d.Set("object_lock_configuration", objectLockConfiguration) + } + + } + return nil +} + +func resourceIBMCOSBucketObjectlockDelete(d *schema.ResourceData, meta interface{}) error { + bucketName := parseObjectLockId(d.Id(), "bucketName") + bucketLocation := parseObjectLockId(d.Id(), "bucketLocation") + instanceCRN := parseObjectLockId(d.Id(), "instanceCRN") + endpointType := parseObjectLockId(d.Id(), "endpointType") + + bxSession, err := meta.(conns.ClientSession).BluemixSession() + if err != nil { + return err + } + + s3Client, err := getS3ClientSession(bxSession, bucketLocation, endpointType, instanceCRN) + if err != nil { + return err + } + putObjectLockConfigurationInput := &s3.PutObjectLockConfigurationInput{ + Bucket: aws.String(bucketName), + ObjectLockConfiguration: &s3.ObjectLockConfiguration{ + ObjectLockEnabled: aws.String("Enabled"), + }, + } + _, err = s3Client.PutObjectLockConfiguration(putObjectLockConfigurationInput) + if err != nil { + return fmt.Errorf("failed to put objectlock configuration on the COS bucket %s, %v", bucketName, err) + } + return nil +} + +func parseObjectLockId(id string, info string) string { + bucketCRN := strings.Split(id, ":meta:")[0] + meta := strings.Split(id, ":meta:")[1] + if info == "bucketName" { + return strings.Split(bucketCRN, ":bucket:")[1] + } + if info == "instanceCRN" { + return fmt.Sprintf("%s::", strings.Split(bucketCRN, ":bucket:")[0]) + } + if info == "bucketCRN" { + return bucketCRN + } + if info == "bucketLocation" { + return strings.Split(meta, ":")[0] + } + if info == "endpointType" { + return strings.Split(meta, ":")[1] + } + if info == "keyName" { + return strings.Split(meta, ":key:")[1] + } + + return parseBucketId(bucketCRN, info) +} + +func objectLockDefaultRetentionSetFunction(objectLockDefaultRetentionList []interface{}) *s3.DefaultRetention { + var defaultRetention *s3.DefaultRetention + var days, years int64 + var mode string + + for _, l := range objectLockDefaultRetentionList { + object_lock_default_retention := s3.DefaultRetention{} + defaultRetentionMap, _ := l.(map[string]interface{}) + + if modeSet, exist := defaultRetentionMap["mode"]; exist { + mode = modeSet.(string) + object_lock_default_retention.Mode = aws.String(mode) + + } + if daysSet, exist := defaultRetentionMap["days"]; exist { + objectLockdays := int64(daysSet.(int)) + if objectLockdays > 0 { + days = objectLockdays + object_lock_default_retention.Days = aws.Int64(days) + } + } + if yearsSet, exist := defaultRetentionMap["years"]; exist { + objectLockyears := int64(yearsSet.(int)) + if objectLockyears > 0 { + years = objectLockyears + object_lock_default_retention.Years = aws.Int64(years) + } + + } + defaultRetention = &object_lock_default_retention + } + + return defaultRetention +} +func objectLockRuleSetFunction(objectLockRuleList []interface{}) *s3.ObjectLockRule { + var rules *s3.ObjectLockRule + + for _, l := range objectLockRuleList { + object_lock_rule := s3.ObjectLockRule{} + ruleMap, _ := l.(map[string]interface{}) + + if defaultRetentionSet, exist := ruleMap["default_retention"]; exist { + object_lock_rule.DefaultRetention = objectLockDefaultRetentionSetFunction(defaultRetentionSet.([]interface{})) + + } + + rules = &object_lock_rule + + } + return rules +} +func objectLockConfigurationSet(objectLockConfigurationList []interface{}) *s3.ObjectLockConfiguration { + var objectLockConfig *s3.ObjectLockConfiguration + + for _, l := range objectLockConfigurationList { + object_lock_configuration := s3.ObjectLockConfiguration{} + configurationMap, _ := l.(map[string]interface{}) + //objectlock enabled + if objectLockEnabledSet, exist := configurationMap["object_lock_enabled"]; exist { + objectLockEnabledValue := objectLockEnabledSet.(string) + if objectLockEnabledValue == "enabled" || objectLockEnabledValue == "Enabled" { + object_lock_configuration.ObjectLockEnabled = aws.String(s3.ObjectLockEnabledEnabled) + } else { + object_lock_configuration.ObjectLockEnabled = aws.String(objectLockEnabledValue) + } + + } + //ObjectLock configuration + if objectLockRuleSet, exist := configurationMap["object_lock_rule"]; exist { + object_lock_configuration.Rule = objectLockRuleSetFunction(objectLockRuleSet.([]interface{})) + + } + objectLockConfig = &object_lock_configuration + + } + return objectLockConfig +} diff --git a/ibm/service/cos/resource_ibm_cos_bucket_objectlock_configuration_test.go b/ibm/service/cos/resource_ibm_cos_bucket_objectlock_configuration_test.go new file mode 100644 index 0000000000..ab8f8a1812 --- /dev/null +++ b/ibm/service/cos/resource_ibm_cos_bucket_objectlock_configuration_test.go @@ -0,0 +1,754 @@ +package cos_test + +import ( + "fmt" + "regexp" + "testing" + + acc "github.com/IBM-Cloud/terraform-provider-ibm/ibm/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +) + +func TestAccIBMCosBucket_Objectlock_Bucket_Enabled(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Bucket_Enabled(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_versioning.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_lock", "true"), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_Without_Rule(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Without_Rule(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled), + Check: resource.ComposeAggregateTestCheckFunc( + testAccCheckIBMCosBucketExists("ibm_resource_instance.instance", "ibm_cos_bucket.bucket", bucketRegionType, bucketRegion, bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_versioning.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_lock", "true"), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_Valid_Mode_and_Days(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + mode := "COMPLIANCE" + days := int64(4) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Valid_Mode_and_Days(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled, mode, days), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_versioning.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_lock", "true"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + ), + }, + }, + }) +} +func TestAccIBMCosBucket_Objectlock_Configuration_Valid_Mode_and_Years(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + mode := "COMPLIANCE" + years := int64(1) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Valid_Mode_and_Years(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled, mode, years), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "bucket_name", bucketName), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "storage_class", bucketClass), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "cross_region_location", bucketRegion), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_versioning.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket.bucket", "object_lock", "true"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_ExistingBucket(t *testing.T) { + bucketRegion := "us" + bucketCRN := acc.BucketCRN + objectLockEnabled := true + mode := "COMPLIANCE" + days := int64(4) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Existing_bucket(bucketCRN, bucketRegion, objectLockEnabled, mode, days), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_enabled", "Enabled"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_Updating_Objectlockrule_Years(t *testing.T) { + bucketRegion := "us" + bucketCRN := acc.BucketCRN + mode := "COMPLIANCE" + years := int64(4) + updated_years := int64(6) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Existing_bucket_Years(bucketCRN, bucketRegion, mode, years), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_enabled", "Enabled"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_rule.0.default_retention.0.years", "4"), + ), + }, + { + Config: testAccCheckIBMCosBucket_Objectlock_Existing_bucket_Years(bucketCRN, bucketRegion, mode, updated_years), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_enabled", "Enabled"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_rule.0.default_retention.0.years", "6"), + ), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_Updating_Objectlockrule_Days(t *testing.T) { + bucketRegion := "us" + bucketCRN := acc.BucketCRN + mode := "COMPLIANCE" + days := int64(3) + updated_days := int64(9) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Existing_bucket_Days(bucketCRN, bucketRegion, mode, days), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_enabled", "Enabled"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_rule.0.default_retention.0.days", "6"), + ), + }, + { + Config: testAccCheckIBMCosBucket_Objectlock_Existing_bucket_Days(bucketCRN, bucketRegion, mode, updated_days), + Check: resource.ComposeAggregateTestCheckFunc( + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_enabled", "Enabled"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.#", "1"), + resource.TestCheckResourceAttr("ibm_cos_bucket_object_lock_configuration.objectlock", "object_lock_configuration.0.object_lock_rule.0.default_retention.0.days", "9"), + ), + }, + }, + }) +} +func TestAccIBMCosBucket_Objectlock_Configuration_Empty(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Empty(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled), + ExpectError: regexp.MustCompile("Error: Missing required argument"), + }, + }, + }) +} +func TestAccIBMCosBucket_Objectlock_Configuration_Without_Mode(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + years := int64(1) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Without_Mode(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled, years), + ExpectError: regexp.MustCompile("Error: Missing required argument"), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_With_Mode_Only(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + mode := "COMPLIANCE" + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_With_Mode_Only(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled, mode), + ExpectError: regexp.MustCompile("MalformedXML: The XML you provided was not well-formed or did not validate against our published schema."), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_With_Versioning_Not_Enabled(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Versioning_not_enabled(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled), + ExpectError: regexp.MustCompile("Error: Missing required argument"), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_Invalid_Mode(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + mode := "INVALID" + years := int64(1) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Invalid_Mode(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled, mode, years), + ExpectError: regexp.MustCompile("MalformedXML: The XML you provided was not well-formed or did not validate against our published schema."), + }, + }, + }) +} + +func TestAccIBMCosBucket_Objectlock_Configuration_Invalid_Days(t *testing.T) { + serviceName := fmt.Sprintf("terraform_%d", acctest.RandIntRange(10, 100)) + bucketName := fmt.Sprintf("terraform%d", acctest.RandIntRange(10, 100)) + bucketRegion := "us" + bucketClass := "standard" + bucketRegionType := "cross_region_location" + objectLockEnabled := true + mode := "COMPLIANCE" + days := int64(-1) + resource.Test(t, resource.TestCase{ + PreCheck: func() { acc.TestAccPreCheck(t) }, + Providers: acc.TestAccProviders, + CheckDestroy: testAccCheckIBMCosBucketDestroy, + Steps: []resource.TestStep{ + { + Config: testAccCheckIBMCosBucket_Objectlock_Invalid_Days(serviceName, bucketName, bucketRegionType, bucketRegion, bucketClass, objectLockEnabled, mode, days), + ExpectError: regexp.MustCompile("MalformedXML: The XML you provided was not well-formed or did not validate against our published schema."), + }, + }, + }) +} + +func testAccCheckIBMCosBucket_Objectlock_Bucket_Enabled(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled) +} + +func testAccCheckIBMCosBucket_Objectlock_Versioning_not_enabled(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_lock = "%t" + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled) +} +func testAccCheckIBMCosBucket_Objectlock_Without_Rule(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled) +} + +func testAccCheckIBMCosBucket_Objectlock_Valid_Mode_and_Days(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool, mode string, days int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + days = "%d" + } + } + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled, mode, days) +} + +func testAccCheckIBMCosBucket_Objectlock_Valid_Mode_and_Years(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool, mode string, years int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + years = "%d" + } + } + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled, mode, years) +} + +func testAccCheckIBMCosBucket_Objectlock_Empty(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled) +} + +func testAccCheckIBMCosBucket_Objectlock_Without_Mode(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool, years int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + years = "%d" + } + } + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled, years) +} + +func testAccCheckIBMCosBucket_Objectlock_With_Mode_Only(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool, mode string) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + } + } + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled, mode) +} + +func testAccCheckIBMCosBucket_Objectlock_Invalid_Mode(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool, mode string, years int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + years = "%d" + } + } + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled, mode, years) +} + +func testAccCheckIBMCosBucket_Objectlock_Invalid_Days(cosServiceName string, bucketName string, regiontype string, region string, storageClass string, objectLockEnabled bool, mode string, days int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource "ibm_resource_instance" "instance" { + name = "%s" + service = "cloud-object-storage" + plan = "standard" + location = "global" + resource_group_id = data.ibm_resource_group.cos_group.id + } + resource "ibm_cos_bucket" "bucket" { + bucket_name = "%s" + resource_instance_id = ibm_resource_instance.instance.id + cross_region_location = "%s" + storage_class = "%s" + object_versioning { + enable = true + } + object_lock = "%t" + } + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.bucket.crn + bucket_location = ibm_cos_bucket.bucket.cross_region_location + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + days = "%d" + } + } + } + } + `, cosServiceName, bucketName, region, storageClass, objectLockEnabled, mode, days) +} + +func testAccCheckIBMCosBucket_Objectlock_Existing_bucket(bucketCrn string, region string, objectLockEnabled bool, mode string, days int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = "%s" + bucket_location = "%s" + object_lock_configuration{ + object_lock_enabled = "%t" + object_lock_rule{ + default_retention{ + mode = "%s" + days = "%d" + } + } + } + } + `, bucketCrn, region, objectLockEnabled, mode, days) +} + +func testAccCheckIBMCosBucket_Objectlock_Existing_bucket_Years(bucketCrn string, region string, mode string, years int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = "%s" + bucket_location = "%s" + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + years = "%d" + } + } + } + } + `, bucketCrn, region, mode, years) +} + +func testAccCheckIBMCosBucket_Objectlock_Existing_bucket_Days(bucketCrn string, region string, mode string, days int64) string { + + return fmt.Sprintf(` + data "ibm_resource_group" "cos_group" { + name = "Default" + } + + resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = "%s" + bucket_location = "%s" + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "%s" + days = "%d" + } + } + } + } + `, bucketCrn, region, mode, days) +} diff --git a/website/docs/d/cos_bucket.html.markdown b/website/docs/d/cos_bucket.html.markdown index f677ac75eb..e10a9504fb 100644 --- a/website/docs/d/cos_bucket.html.markdown +++ b/website/docs/d/cos_bucket.html.markdown @@ -95,6 +95,12 @@ resource "ibm_cos_bucket" "smart-us-south" { } ``` + +# ibm_cos_objectlock_configuration + +Retrive an IBM Cloud Object Storage bucket with objectlock enabled and objectlock configuration set on the bucket.It allows objectlock configuration to be updated or deleted.But it does not allow to disable objectlock as objectloock cannot be disabled once enabled on a bucket. + + ## Argument reference Review the argument references that you can specify for your data source. @@ -105,6 +111,7 @@ Review the argument references that you can specify for your data source. - `resource_instance_id` - (Required, String) The ID of the IBM Cloud Object Storage service instance for which you want to create a bucket. - `storage_class`- (Optional, String) Storage class of the bucket. Supported values are `standard`, `vault`, `cold`, `smart` for `standard` and `lite` COS plans, `onerate_active` for `cos-one-rate-plan` COS instance. - `satellite_location_id` - (Optional, String) satellite location id. Provided by end users. +- `object_lock` - (Optional, String) Specifies Objectlock status. ## Attribute reference In addition to all argument reference list, you can access the following attribute references after your data source is created. @@ -179,6 +186,22 @@ In addition to all argument reference list, you can access the following attribu - `deletemarker_replication_status`- (Bool) Specifies whether Object storage replicates delete markers. Specify true for Enabling it or false for Disabling it. - `destination_bucket_crn`- (String) The CRN of your destination bucket that you want to replicate to. +- `object_lock_configuration`- (Required, List) Nested block have the following structure: + + Nested scheme for `object_lock_configuration`: + - `object_lock_enabled`- (String) Indicates whether this bucket has an Object Lock configuration enabled. Defaults to Enabled. Valid values: Enabled. + - `object_lock_rule`- (List) Objectlockrule has following arguement: + + Nested scheme for `object_lock_rule`: + - `default_retention`- (Required) Configuration block for specifying the default Object Lock retention settings for new objects placed in the specified bucket + Nested scheme for `default_retention`: + - `mode`- (String) Default Object Lock retention mode you want to apply to new objects placed in the specified bucket. Supported values: COMPLIANCE. + - `days`- (Int) Specifies number of days after which the object can be deleted from the COS bucket. + - `years`- (Int) Specifies number of years after which the object can be deleted from the COS bucket. +**Note:** + + Either days or years should be provided for default retention, both cannot be used simultaneoulsy. + - `single_site_location` - (String) The location to create a single site bucket. - `storage_class` - (String) The storage class of the bucket. - `s3_endpoint_public` - (String) Public endpoint for cos bucket. diff --git a/website/docs/d/cos_bucket_object.html.markdown b/website/docs/d/cos_bucket_object.html.markdown index 46137e8385..d4787a3335 100644 --- a/website/docs/d/cos_bucket_object.html.markdown +++ b/website/docs/d/cos_bucket_object.html.markdown @@ -54,3 +54,6 @@ In addition to all argument reference list, you can access the following attribu - `etag` - (String) Computed MD5 hexdigest of an object content. - `last_modified` - (Timestamp) Last modified date of an object in a GMT formatted date. - `object_sql_url` - (String) Access the object using an SQL Query instance. The SQL URL is a reference URL used inside an SQL statement. The reference URL is used to perform queries against objects storing structured data. +- `object_lock_mode` - (String) This is the retention mode for an object. +- `object_lock_retain_until_date` - (String) A date after which the object can be deleted from the COS bucket. +- `object_lock_legal_hold_status` - (String) If the value of this attribute is **ON**, then the object cannot be deleted from the COS bucket. diff --git a/website/docs/r/cos_bucket.html.markdown b/website/docs/r/cos_bucket.html.markdown index 6ffaa8b395..379955f17e 100644 --- a/website/docs/r/cos_bucket.html.markdown +++ b/website/docs/r/cos_bucket.html.markdown @@ -398,6 +398,34 @@ resource "ibm_cos_bucket" "cos_bucket_onerate" { ``` +# ibm_cos_objectlock_configuration + +COS objectlock feature enables user to store the object in a bucket with an extra layer of protection against object changes and deletion.Object Lock can help prevent objects from being deleted or overwritten for a fixed amount of time or indefinitely by setting up retention period and legalhold for an object. + +## Example usage + +```terraform +data "ibm_resource_group" "cos_group" { + name = "cos-resource-group" +} + +resource "ibm_resource_instance" "cos_instance" { + name = "cos-instance" + resource_group_id = data.ibm_resource_group.cos_group.id + service = "cloud-object-storage" +} + +resource "ibm_cos_bucket" "cos_bucket" { + bucket_name = "a-standard-bucket" + resource_instance_id = data.ibm_resource_instance.cos_instance.id + bucket_region = "us-south" + storage_class = "Standard" + object_versioning { + enable = true + } + object_lock = true +} +``` ## Argument reference @@ -501,7 +529,12 @@ Review the argument references that you can specify for your resource. - `single_site_location` - (Optional, String) The location for a single site bucket. Supported values are: `ams03`, `che01`, `hkg02`, `mel01`, `mex01`, `mil01`, `mon01`, `osl01`, `par01`, `sjc04`, `sao01`, `seo01`, `sng01`, and `tor01`. If you set this parameter, do not set `region_location` or `cross_region_location` at the same time. - `storage_class` - (Optional, String) The storage class that you want to use for the bucket. Supported values are `standard`, `vault`, `cold` and `smart` for `standard` and `lite` COS plans, `onerate_active` for `cos-one-rate-plan` COS plan.For more information, about storage classes, see [Use storage classes](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-classes).`storage_class` should not be used with Satellite location id. - `satellite_location_id` - (Optional, String) satellite location id. Provided by end users. +- `object_lock` - (Optional, Bool) Enables objectlock feature on a COS bucket. + **Note:** + - To enable objectlock on a bucket , object_versioning should be enabled. + + ## Attribute reference In addition to all argument reference list, you can access the following attribute reference after your resource is created. diff --git a/website/docs/r/cos_bucket_object.html.markdown b/website/docs/r/cos_bucket_object.html.markdown index e59ec48023..5613454dc7 100644 --- a/website/docs/r/cos_bucket_object.html.markdown +++ b/website/docs/r/cos_bucket_object.html.markdown @@ -54,6 +54,47 @@ resource "ibm_cos_bucket_object" "file" { etag = filemd5("${path.module}/object.json") } ``` +# Objectlock + +**Note:** +Objectlock must be enabled on the bucket to configure `object_lock_mode` , `object_lock_retain_until_date` , `object_lock_legal_hold_status` on the object. + +## Example usage + +```terraform +data "ibm_resource_group" "cos_group" { + name = "cos-resource-group" +} + +resource "ibm_resource_instance" "cos_instance" { + name = "cos-instance" + resource_group_id = data.ibm_resource_group.cos_group.id + service = "cloud-object-storage" + plan = "standard" + location = "global" +} + +resource "ibm_cos_bucket" "cos_bucket" { + bucket_name = "my-bucket" + resource_instance_id = ibm_resource_instance.cos_instance.id + region_location = "us-east" + storage_class = "standard" + object_versioning { + enable = true + } + object_lock = true +} + +resource "ibm_cos_bucket_object" "cos_object_objectlock" { + bucket_crn = data.ibm_cos_bucket.cos_bucket.crn + bucket_location = data.ibm_cos_bucket.cos_bucket.bucket_region + key = "object.json" + object_lock_mode = "COMPLIANCE" + object_lock_retain_until_date = "2023-02-15T18:00:00Z" + object_lock_legal_hold_status = "ON" +} +``` + ## Argument reference Review the argument references that you can specify for your resource. diff --git a/website/docs/r/cos_bucket_objectlock_configuration.html.markdown b/website/docs/r/cos_bucket_objectlock_configuration.html.markdown new file mode 100644 index 0000000000..701c948e0c --- /dev/null +++ b/website/docs/r/cos_bucket_objectlock_configuration.html.markdown @@ -0,0 +1,149 @@ +--- + +subcategory: "Object Storage" +layout: "ibm" +page_title: "IBM : Cloud Object Storage Object Lock Configuration" +description: + "Manages IBM Cloud Object Storage Object Lock Configuration" +--- + +# ibm_cos_bucket_object_lock_configuration +Provides an Object Lock configuration resource. This resource is used to configure a default retention period for objects placed in the specified bucket. To enable objectlock for a new bucket see [ibm_cos_bucket](https://registry.terraform.io/providers/IBM-Cloud/ibm/latest/docs/resources/cos_bucket). + + + +**Note:** +To configure ObjectLock on a bucket, you must enable object versioning on bucket by using the [Versioning objects](https://cloud.ibm.com/docs/cloud-object-storage?topic=cloud-object-storage-versioning). + +--- + +## Example usage +The following example demonstrates creating a bucket with object lock enabled with default retention. + +```terraform +data "ibm_resource_group" "cos_group" { + name = "cos-resource-group" +} + +resource "ibm_resource_instance" "cos_instance" { + name = "cos-instance" + resource_group_id = data.ibm_resource_group.cos_group.id + service = "cloud-object-storage" + plan = "standard" + location = "global" +} + +resource "ibm_cos_bucket" "cos_bucket" { + bucket_name = "a-standard-bucket" + resource_instance_id = data.ibm_resource_instance.cos_instance.id + bucket_region = "us-south" + storage_class = "Standard" + object_versioning { + enable = true + } + object_lock = true +} + +resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.cos_bucket.crn + bucket_location = ibm_cos_bucket.cos_bucket.bucket_region + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "COMPLIANCE" + days = 4 + } + } + } +} + +``` +# Enabling Objectlockconfguration on an existing bucket +To enable objectlock configuration on an existing bucket , create a COS bucket with object versioning enabled and pass the crn of the COS bucket and location of the bucket to `ibm_cos_bucket_object_lock_configuration.bucket_crn` and `ibm_cos_bucket_object_lock_configuration.bucket_location` as shown in the example. + +## Example usage + +```terraform +// To only enable objectlock configuration on an existing bucket + +resource "ibm_cos_bucket" "cos_bucket" { + bucket_name = "a-standard-bucket" + resource_instance_id = data.ibm_resource_instance.cos_instance.id + bucket_region = "us-south" + storage_class = "Standard" + object_versioning { + enable = true + } +} + +resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.cos_bucket.crn + bucket_location = ibm_cos_bucket.cos_bucket.bucket_region + object_lock_configuration{ + object_lock_enabled = "Enabled" + } +} + +// To enable object lock configuration and set default retention on a bucket + +resource ibm_cos_bucket_object_lock_configuration "objectlock" { + bucket_crn = ibm_cos_bucket.cos_bucket.crn + bucket_location = ibm_cos_bucket.cos_bucket.bucket_region + object_lock_configuration{ + object_lock_enabled = "Enabled" + object_lock_rule{ + default_retention{ + mode = "COMPLIANCE" + days = 4 + } + } + } +} + + +``` + +## Argument reference +Review the argument references that you can specify for your resource. +- `bucket_crn` - (Required, Forces new resource, String) The CRN of the COS bucket. +- `bucket_location` - (Required, Forces new resource, String) The location of the COS bucket. +- `endpoint_type`- (Optional, String) The type of the endpoint either `public` or `private` or `direct` to be used for buckets. Default value is `public`. +- `object_lock_configuration`- (Required, List) Nested block have the following structure: + + Nested scheme for `object_lock_configuration`: + - `object_lock_enabled`- (String) Indicates whether this bucket has an Object Lock configuration enabled. Defaults to Enabled. Valid values: Enabled. + - `object_lock_rule`- (List) Objectlockrule has following arguement: + + Nested scheme for `object_lock_rule`: + - `default_retention`- (Required) Configuration block for specifying the default Object Lock retention settings for new objects placed in the specified bucket + Nested scheme for `default_retention`: + - `mode`- (String) Default Object Lock retention mode you want to apply to new objects placed in the specified bucket. Supported values: COMPLIANCE. + - `days`- (Int) Specifies number of days after which the object can be deleted from the COS bucket. + - `years`- (Int) Specifies number of years after which the object can be deleted from the COS bucket. + +## Attribute reference +In addition to all argument reference list, you can access the following attribute reference after your resource is created. + +- `crn` - (String) The CRN of the bucket. +- `id` - (String) The ID of the bucket. + +## Import IBM COS Bucket +The `ibm_cos_bucket_object_lock_configuration` resource can be imported by using the `id`. The ID is formed from the `CRN` (Cloud Resource Name). The `CRN` and bucket location can be found on the portal. + +id = `$CRN:meta:$bucketlocation:$endpointtype` + +**Syntax** + +``` +$ terraform import ibm_cos_bucket_object_lock_configuration.objectlock `$CRN:meta:$bucketlocation:public` + +``` + +**Example** + +``` + +$ terraform import ibm_cos_bucket_object_lock_configuration.objectlock crn:v1:bluemix:public:cloud-object-storage:global:a/4ea1882a2d3401ed1e459979941966ea:31fa970d-51d0-4b05-893e-251cba75a7b3:bucket:mybucketname:meta:us-south:public + +``` \ No newline at end of file