Skip to content

Commit e855937

Browse files
authored
feat(objectstorage): support object lock in bucket (#1325)
relates to STACKITTPR-546
1 parent 2adf8f0 commit e855937

File tree

8 files changed

+98
-1
lines changed

8 files changed

+98
-1
lines changed

docs/data-sources/objectstorage_bucket.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ data "stackit_objectstorage_bucket" "example" {
3434
### Read-Only
3535

3636
- `id` (String) Terraform's internal data source identifier. It is structured as "`project_id`,`region`,`name`".
37+
- `object_lock` (Boolean) Enable Object Lock on this bucket. Can only be set at creation time. Requires an active project-level compliance lock.
3738
- `url_path_style` (String)
3839
- `url_virtual_hosted_style` (String)

docs/resources/objectstorage_bucket.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@ resource "stackit_objectstorage_bucket" "example" {
1818
name = "example-bucket"
1919
}
2020
21+
## With compliance lock
22+
resource "stackit_objectstorage_compliance_lock" "example_with_lock" {
23+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
24+
}
25+
26+
resource "stackit_objectstorage_bucket" "example_with_lock" {
27+
depends_on = [stackit_objectstorage_compliance_lock.example_with_lock]
28+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
29+
name = "example-bucket-with-lock"
30+
object_lock = true
31+
}
32+
33+
2134
# Only use the import statement, if you want to import an existing objectstorage bucket
2235
import {
2336
to = stackit_objectstorage_bucket.import-example
@@ -35,6 +48,7 @@ import {
3548

3649
### Optional
3750

51+
- `object_lock` (Boolean) Enable Object Lock on this bucket. Can only be set at creation time. Requires an active project-level compliance lock.
3852
- `region` (String) The resource region. If not defined, the provider region is used.
3953

4054
### Read-Only

examples/resources/stackit_objectstorage_bucket/resource.tf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,19 @@ resource "stackit_objectstorage_bucket" "example" {
33
name = "example-bucket"
44
}
55

6+
## With compliance lock
7+
resource "stackit_objectstorage_compliance_lock" "example_with_lock" {
8+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
9+
}
10+
11+
resource "stackit_objectstorage_bucket" "example_with_lock" {
12+
depends_on = [stackit_objectstorage_compliance_lock.example_with_lock]
13+
project_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
14+
name = "example-bucket-with-lock"
15+
object_lock = true
16+
}
17+
18+
619
# Only use the import statement, if you want to import an existing objectstorage bucket
720
import {
821
to = stackit_objectstorage_bucket.import-example

stackit/internal/services/objectstorage/bucket/datasource.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ func (r *bucketDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
6363
"id": "Terraform's internal data source identifier. It is structured as \"`project_id`,`region`,`name`\".",
6464
"name": "The bucket name. It must be DNS conform.",
6565
"project_id": "STACKIT Project ID to which the bucket is associated.",
66+
"object_lock": "Enable Object Lock on this bucket. Can only be set at creation time. Requires an active project-level compliance lock.",
6667
"url_path_style": "URL in path style.",
6768
"url_virtual_hosted_style": "URL in virtual hosted style.",
6869
"region": "The resource region. If not defined, the provider region is used.",
@@ -90,6 +91,10 @@ func (r *bucketDataSource) Schema(_ context.Context, _ datasource.SchemaRequest,
9091
validate.NoSeparator(),
9192
},
9293
},
94+
"object_lock": schema.BoolAttribute{
95+
Description: descriptions["object_lock"],
96+
Computed: true,
97+
},
9398
"url_path_style": schema.StringAttribute{
9499
Computed: true,
95100
},

stackit/internal/services/objectstorage/bucket/resource.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import (
77
"net/http"
88
"strings"
99

10+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/booldefault"
11+
"github.com/hashicorp/terraform-plugin-framework/resource/schema/boolplanmodifier"
1012
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion"
1113
objectstorageUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/utils"
1214

@@ -41,6 +43,7 @@ type Model struct {
4143
URLPathStyle types.String `tfsdk:"url_path_style"`
4244
URLVirtualHostedStyle types.String `tfsdk:"url_virtual_hosted_style"`
4345
Region types.String `tfsdk:"region"`
46+
ObjectLock types.Bool `tfsdk:"object_lock"`
4447
}
4548

4649
// NewBucketResource is a helper function to simplify the provider implementation.
@@ -112,6 +115,7 @@ func (r *bucketResource) Schema(_ context.Context, _ resource.SchemaRequest, res
112115
"id": "Terraform's internal resource identifier. It is structured as \"`project_id`,`region`,`name`\".",
113116
"name": "The bucket name. It must be DNS conform.",
114117
"project_id": "STACKIT Project ID to which the bucket is associated.",
118+
"object_lock": "Enable Object Lock on this bucket. Can only be set at creation time. Requires an active project-level compliance lock.",
115119
"url_path_style": "URL in path style.",
116120
"url_virtual_hosted_style": "URL in virtual hosted style.",
117121
"region": "The resource region. If not defined, the provider region is used.",
@@ -150,6 +154,15 @@ func (r *bucketResource) Schema(_ context.Context, _ resource.SchemaRequest, res
150154
validate.NoSeparator(),
151155
},
152156
},
157+
"object_lock": schema.BoolAttribute{
158+
Description: descriptions["object_lock"],
159+
Optional: true,
160+
Computed: true,
161+
Default: booldefault.StaticBool(false),
162+
PlanModifiers: []planmodifier.Bool{
163+
boolplanmodifier.RequiresReplace(),
164+
},
165+
},
153166
"url_path_style": schema.StringAttribute{
154167
Computed: true,
155168
},
@@ -196,7 +209,7 @@ func (r *bucketResource) Create(ctx context.Context, req resource.CreateRequest,
196209
}
197210

198211
// Create new bucket
199-
_, err = r.client.CreateBucket(ctx, projectId, region, bucketName).Execute()
212+
_, err = r.client.CreateBucket(ctx, projectId, region, bucketName).ObjectLockEnabled(model.ObjectLock.ValueBool()).Execute()
200213
if err != nil {
201214
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating bucket", fmt.Sprintf("Calling API: %v", err))
202215
return
@@ -367,6 +380,7 @@ func mapFields(bucketResp *objectstorage.GetBucketResponse, model *Model, region
367380
model.URLPathStyle = types.StringPointerValue(bucket.UrlPathStyle)
368381
model.URLVirtualHostedStyle = types.StringPointerValue(bucket.UrlVirtualHostedStyle)
369382
model.Region = types.StringValue(region)
383+
model.ObjectLock = types.BoolPointerValue(bucket.ObjectLockEnabled)
370384
return nil
371385
}
372386

stackit/internal/services/objectstorage/bucket/resource_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,12 +56,14 @@ func TestMapFields(t *testing.T) {
5656
Bucket: &objectstorage.Bucket{
5757
UrlPathStyle: utils.Ptr("url/path/style"),
5858
UrlVirtualHostedStyle: utils.Ptr("url/virtual/hosted/style"),
59+
ObjectLockEnabled: utils.Ptr(true),
5960
},
6061
},
6162
Model{
6263
Id: types.StringValue(id),
6364
Name: types.StringValue("bname"),
6465
ProjectId: types.StringValue("pid"),
66+
ObjectLock: types.BoolValue(true),
6567
URLPathStyle: types.StringValue("url/path/style"),
6668
URLVirtualHostedStyle: types.StringValue("url/virtual/hosted/style"),
6769
Region: types.StringValue("eu01"),

stackit/internal/services/objectstorage/objectstorage_acc_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ var testConfigVarsMin = config.Variables{
2929
"objectstorage_bucket_name": config.StringVariable(fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(20, acctest.CharSetAlpha))),
3030
"objectstorage_credentials_group_name": config.StringVariable(fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(20, acctest.CharSetAlpha))),
3131
"expiration_timestamp": config.StringVariable(fmt.Sprintf("%d-01-02T03:04:05Z", time.Now().Year()+1)),
32+
33+
"objectstorage_bucket_name_with_lock": config.StringVariable(fmt.Sprintf("tf-acc-test-%s", acctest.RandStringFromCharSet(20, acctest.CharSetAlpha))),
34+
"object_lock": config.BoolVariable(true),
3235
}
3336

3437
func TestAccObjectStorageResourceMin(t *testing.T) {
@@ -46,6 +49,7 @@ func TestAccObjectStorageResourceMin(t *testing.T) {
4649
resource.TestCheckResourceAttr("stackit_objectstorage_bucket.bucket", "name", testutil.ConvertConfigVariable(testConfigVarsMin["objectstorage_bucket_name"])),
4750
resource.TestCheckResourceAttrSet("stackit_objectstorage_bucket.bucket", "url_path_style"),
4851
resource.TestCheckResourceAttrSet("stackit_objectstorage_bucket.bucket", "url_virtual_hosted_style"),
52+
resource.TestCheckResourceAttr("stackit_objectstorage_bucket.bucket", "object_lock", "false"),
4953

5054
// Credentials group data
5155
resource.TestCheckResourceAttr("stackit_objectstorage_credentials_group.credentials_group", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
@@ -85,6 +89,13 @@ func TestAccObjectStorageResourceMin(t *testing.T) {
8589
// compliance lock
8690
resource.TestCheckResourceAttr("stackit_objectstorage_compliance_lock.compliance_lock", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
8791
resource.TestCheckResourceAttrSet("stackit_objectstorage_compliance_lock.compliance_lock", "max_retention_days"),
92+
93+
// object storage with object lock enabled
94+
resource.TestCheckResourceAttr("stackit_objectstorage_bucket.bucket_object_lock", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
95+
resource.TestCheckResourceAttr("stackit_objectstorage_bucket.bucket_object_lock", "name", testutil.ConvertConfigVariable(testConfigVarsMin["objectstorage_bucket_name_with_lock"])),
96+
resource.TestCheckResourceAttrSet("stackit_objectstorage_bucket.bucket_object_lock", "url_path_style"),
97+
resource.TestCheckResourceAttrSet("stackit_objectstorage_bucket.bucket_object_lock", "url_virtual_hosted_style"),
98+
resource.TestCheckResourceAttr("stackit_objectstorage_bucket.bucket_object_lock", "object_lock", testutil.ConvertConfigVariable(testConfigVarsMin["object_lock"])),
8899
),
89100
},
90101
// Data source
@@ -116,6 +127,10 @@ func TestAccObjectStorageResourceMin(t *testing.T) {
116127
}
117128
data "stackit_objectstorage_compliance_lock" "compliance_lock" {
118129
project_id = stackit_objectstorage_compliance_lock.compliance_lock.project_id
130+
}
131+
data "stackit_objectstorage_bucket" "bucket_object_lock" {
132+
project_id = stackit_objectstorage_bucket.bucket_object_lock.project_id
133+
name = stackit_objectstorage_bucket.bucket_object_lock.name
119134
}`,
120135
testutil.ObjectStorageProviderConfig()+resourceMinConfig,
121136
),
@@ -134,6 +149,10 @@ func TestAccObjectStorageResourceMin(t *testing.T) {
134149
"stackit_objectstorage_bucket.bucket", "url_virtual_hosted_style",
135150
"data.stackit_objectstorage_bucket.bucket", "url_virtual_hosted_style",
136151
),
152+
resource.TestCheckResourceAttrPair(
153+
"stackit_objectstorage_bucket.bucket", "object_lock",
154+
"data.stackit_objectstorage_bucket.bucket", "object_lock",
155+
),
137156

138157
// Credentials group data
139158
resource.TestCheckResourceAttr("data.stackit_objectstorage_credentials_group.credentials_group", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
@@ -197,6 +216,25 @@ func TestAccObjectStorageResourceMin(t *testing.T) {
197216
// Compliance lock
198217
resource.TestCheckResourceAttr("data.stackit_objectstorage_compliance_lock.compliance_lock", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
199218
resource.TestCheckResourceAttrSet("data.stackit_objectstorage_compliance_lock.compliance_lock", "max_retention_days"),
219+
220+
// Bucket data with object lock
221+
resource.TestCheckResourceAttr("data.stackit_objectstorage_bucket.bucket_object_lock", "project_id", testutil.ConvertConfigVariable(testConfigVarsMin["project_id"])),
222+
resource.TestCheckResourceAttrPair(
223+
"stackit_objectstorage_bucket.bucket_object_lock", "name",
224+
"data.stackit_objectstorage_bucket.bucket_object_lock", "name",
225+
),
226+
resource.TestCheckResourceAttrPair(
227+
"stackit_objectstorage_bucket.bucket_object_lock", "url_path_style",
228+
"data.stackit_objectstorage_bucket.bucket_object_lock", "url_path_style",
229+
),
230+
resource.TestCheckResourceAttrPair(
231+
"stackit_objectstorage_bucket.bucket_object_lock", "url_virtual_hosted_style",
232+
"data.stackit_objectstorage_bucket.bucket_object_lock", "url_virtual_hosted_style",
233+
),
234+
resource.TestCheckResourceAttrPair(
235+
"stackit_objectstorage_bucket.bucket_object_lock", "object_lock",
236+
"data.stackit_objectstorage_bucket.bucket_object_lock", "object_lock",
237+
),
200238
),
201239
},
202240
// Import

stackit/internal/services/objectstorage/testfiles/resource-min.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ variable "objectstorage_bucket_name" {}
44
variable "objectstorage_credentials_group_name" {}
55
variable "expiration_timestamp" {}
66

7+
variable "objectstorage_bucket_name_with_lock" {}
8+
variable "object_lock" {}
9+
710
resource "stackit_objectstorage_bucket" "bucket" {
811
project_id = var.project_id
912
name = var.objectstorage_bucket_name
@@ -27,4 +30,11 @@ resource "stackit_objectstorage_credential" "credential_time" {
2730

2831
resource "stackit_objectstorage_compliance_lock" "compliance_lock" {
2932
project_id = var.project_id
33+
}
34+
35+
resource "stackit_objectstorage_bucket" "bucket_object_lock" {
36+
depends_on = [stackit_objectstorage_compliance_lock.compliance_lock]
37+
project_id = var.project_id
38+
name = var.objectstorage_bucket_name_with_lock
39+
object_lock = var.object_lock
3040
}

0 commit comments

Comments
 (0)