Skip to content

Commit bdeb987

Browse files
authored
feat(sfs): add snapshot_policy_id to sfs_resource_pool (#1444)
relates to STACKITTPR-629
1 parent fc72d27 commit bdeb987

10 files changed

Lines changed: 166 additions & 25 deletions

File tree

docs/data-sources/sfs_resource_pool.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,13 @@ data "stackit_sfs_resource_pool" "resourcepool" {
4444
- `performance_class_downgradable_at` (String) Time when the performance class can be downgraded again.
4545
- `size_gigabytes` (Number) Size of the resource pool (unit: gigabytes)
4646
- `size_reducible_at` (String) Time when the size can be reduced again.
47+
- `snapshot_policy` (Attributes) Name of the snapshot policy. (see [below for nested schema](#nestedatt--snapshot_policy))
4748
- `snapshots_are_visible` (Boolean) If set to true, snapshots are visible and accessible to users. (default: false)
49+
50+
<a id="nestedatt--snapshot_policy"></a>
51+
### Nested Schema for `snapshot_policy`
52+
53+
Read-Only:
54+
55+
- `id` (String) ID of the snapshot policy.
56+
- `name` (String) Name of the snapshot policy.

docs/resources/sfs_resource_pool.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,21 @@ import {
5151
### Optional
5252

5353
- `region` (String) The resource region. If not defined, the provider region is used.
54+
- `snapshot_policy` (Attributes) Name of the snapshot policy. (see [below for nested schema](#nestedatt--snapshot_policy))
5455
- `snapshots_are_visible` (Boolean) If set to true, snapshots are visible and accessible to users. (default: false)
5556

5657
### Read-Only
5758

5859
- `id` (String) Terraform's internal resource ID. It is structured as "`project_id`,`region`,`resource_pool_id`".
5960
- `resource_pool_id` (String) Resource pool ID
61+
62+
<a id="nestedatt--snapshot_policy"></a>
63+
### Nested Schema for `snapshot_policy`
64+
65+
Required:
66+
67+
- `id` (String) ID of the snapshot policy.
68+
69+
Read-Only:
70+
71+
- `name` (String) Name of the snapshot policy.

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ require (
4141
github.com/stackitcloud/stackit-sdk-go/services/serverupdate v1.5.2
4242
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.19.0
4343
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.5.3
44-
github.com/stackitcloud/stackit-sdk-go/services/sfs v0.9.0
44+
github.com/stackitcloud/stackit-sdk-go/services/sfs v0.10.0
4545
github.com/stackitcloud/stackit-sdk-go/services/ske v1.14.0
4646
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.10.0
4747
github.com/teambition/rrule-go v1.8.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -728,8 +728,8 @@ github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.19.0 h1:VDGfv4
728728
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.19.0/go.mod h1:xJNa86o89czK2o60+s46vz8+NSKDTbQduFGFaztJnsA=
729729
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.5.3 h1:LM/u2bnB5B9xhSX8S3fC5gYu3VPvWZa+xDndhpI2jc4=
730730
github.com/stackitcloud/stackit-sdk-go/services/serviceenablement v1.5.3/go.mod h1:fXq3TmVLb4JMSve989NFFViMFoYa83s7M3hJWgN6mdQ=
731-
github.com/stackitcloud/stackit-sdk-go/services/sfs v0.9.0 h1:JWAFnskRbNKT8x62pZcAMCC+p5hyTEkAyxqFwy39jFA=
732-
github.com/stackitcloud/stackit-sdk-go/services/sfs v0.9.0/go.mod h1:jMlBoXqrPNX5nXbo6oT7exalqilw1jiLPoIp4Cn0CdI=
731+
github.com/stackitcloud/stackit-sdk-go/services/sfs v0.10.0 h1:xqYelSLm+zxaN0H2EIHXptMTF4ehWrUUlXxQMklXlU0=
732+
github.com/stackitcloud/stackit-sdk-go/services/sfs v0.10.0/go.mod h1:jMlBoXqrPNX5nXbo6oT7exalqilw1jiLPoIp4Cn0CdI=
733733
github.com/stackitcloud/stackit-sdk-go/services/ske v1.14.0 h1:Zy3yxmHzW+ydu1naeESMh8psXhUglkOHZ+/F3SJMwX8=
734734
github.com/stackitcloud/stackit-sdk-go/services/ske v1.14.0/go.mod h1:TbqmZhLMofmfl+HhVl6oHYcI3zvXTm1vRjN3A/fOkM4=
735735
github.com/stackitcloud/stackit-sdk-go/services/sqlserverflex v1.10.0 h1:angvO3z0TGqZtdwTDsG/tgTw9hxB76A6leUsiUXQtME=

stackit/internal/services/sfs/resourcepool/datasource.go

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,19 @@ var (
3131
)
3232

3333
type dataSourceModel struct {
34-
Id types.String `tfsdk:"id"` // needed by TF
35-
ProjectId types.String `tfsdk:"project_id"`
36-
ResourcePoolId types.String `tfsdk:"resource_pool_id"`
37-
AvailabilityZone types.String `tfsdk:"availability_zone"`
38-
IpAcl types.List `tfsdk:"ip_acl"`
39-
Name types.String `tfsdk:"name"`
40-
PerformanceClass types.String `tfsdk:"performance_class"`
41-
SizeGigabytes types.Int32 `tfsdk:"size_gigabytes"`
42-
SizeReducibleAt types.String `tfsdk:"size_reducible_at"`
43-
PerformanceClassDowngradableAt types.String `tfsdk:"performance_class_downgradable_at"`
44-
Region types.String `tfsdk:"region"`
45-
SnapshotsAreVisible types.Bool `tfsdk:"snapshots_are_visible"`
34+
Id types.String `tfsdk:"id"` // needed by TF
35+
ProjectId types.String `tfsdk:"project_id"`
36+
ResourcePoolId types.String `tfsdk:"resource_pool_id"`
37+
AvailabilityZone types.String `tfsdk:"availability_zone"`
38+
IpAcl types.List `tfsdk:"ip_acl"`
39+
Name types.String `tfsdk:"name"`
40+
PerformanceClass types.String `tfsdk:"performance_class"`
41+
SizeGigabytes types.Int32 `tfsdk:"size_gigabytes"`
42+
SizeReducibleAt types.String `tfsdk:"size_reducible_at"`
43+
PerformanceClassDowngradableAt types.String `tfsdk:"performance_class_downgradable_at"`
44+
Region types.String `tfsdk:"region"`
45+
SnapshotsAreVisible types.Bool `tfsdk:"snapshots_are_visible"`
46+
SnapshotPolicy *SnapshotPolicyModel `tfsdk:"snapshot_policy"`
4647
}
4748

4849
type resourcePoolDataSource struct {
@@ -191,6 +192,20 @@ func (r *resourcePoolDataSource) Schema(_ context.Context, _ datasource.SchemaRe
191192
Computed: true,
192193
Description: "If set to true, snapshots are visible and accessible to users. (default: false)",
193194
},
195+
"snapshot_policy": schema.SingleNestedAttribute{
196+
Description: `Name of the snapshot policy.`,
197+
Computed: true,
198+
Attributes: map[string]schema.Attribute{
199+
"id": schema.StringAttribute{
200+
Description: "ID of the snapshot policy.",
201+
Computed: true,
202+
},
203+
"name": schema.StringAttribute{
204+
Description: "Name of the snapshot policy.",
205+
Computed: true,
206+
},
207+
},
208+
},
194209
"region": schema.StringAttribute{
195210
// the region cannot be found automatically, so it has to be passed
196211
Optional: true,
@@ -247,5 +262,12 @@ func mapDataSourceFields(ctx context.Context, region string, resourcePool *sfs.R
247262
model.SizeReducibleAt = types.StringValue(t.Format(time.RFC3339))
248263
}
249264

265+
if snapshotPolicy := resourcePool.SnapshotPolicy.Get(); snapshotPolicy != nil {
266+
model.SnapshotPolicy = &SnapshotPolicyModel{
267+
Id: types.StringPointerValue(snapshotPolicy.Id),
268+
Name: types.StringPointerValue(snapshotPolicy.Name),
269+
}
270+
}
271+
250272
return nil
251273
}

stackit/internal/services/sfs/resourcepool/datasource_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ func TestMapDatasourceFields(t *testing.T) {
7474
Space: &sfs.ResourcePoolSpace{
7575
SizeGigabytes: utils.Ptr[int32](42),
7676
},
77+
SnapshotPolicy: *sfs.NewNullableResourcePoolSnapshotPolicy(&sfs.ResourcePoolSnapshotPolicy{
78+
Id: new("snapshot-id"),
79+
Name: new("snapshot-name"),
80+
}),
7781
State: new("state"),
7882
},
7983
expected: &dataSourceModel{
@@ -92,6 +96,10 @@ func TestMapDatasourceFields(t *testing.T) {
9296
Region: testRegion,
9397
SizeReducibleAt: testTimePlus1h,
9498
PerformanceClassDowngradableAt: testTime,
99+
SnapshotPolicy: &SnapshotPolicyModel{
100+
Id: types.StringValue("snapshot-id"),
101+
Name: types.StringValue("snapshot-name"),
102+
},
95103
},
96104
isValid: true,
97105
},

stackit/internal/services/sfs/resourcepool/resource.go

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"strings"
1010

1111
"github.com/hashicorp/terraform-plugin-framework-validators/listvalidator"
12+
"github.com/hashicorp/terraform-plugin-framework/attr"
1213
"github.com/hashicorp/terraform-plugin-framework/diag"
1314
"github.com/hashicorp/terraform-plugin-framework/resource"
1415
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -17,6 +18,7 @@ import (
1718
"github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier"
1819
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1920
"github.com/hashicorp/terraform-plugin-framework/types"
21+
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
2022
"github.com/hashicorp/terraform-plugin-log/tflog"
2123
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
2224
sfs "github.com/stackitcloud/stackit-sdk-go/services/sfs/v1api"
@@ -27,6 +29,7 @@ import (
2729
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/features"
2830
sfsUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/sfs/utils"
2931
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
32+
stringplanmodifierUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils/planmodifiers/stringplanmodifier"
3033
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
3134
)
3235

@@ -47,10 +50,21 @@ type Model struct {
4750
Name types.String `tfsdk:"name"`
4851
PerformanceClass types.String `tfsdk:"performance_class"`
4952
SizeGigabytes types.Int32 `tfsdk:"size_gigabytes"`
53+
SnapshotPolicy types.Object `tfsdk:"snapshot_policy"`
5054
Region types.String `tfsdk:"region"`
5155
SnapshotsAreVisible types.Bool `tfsdk:"snapshots_are_visible"`
5256
}
5357

58+
type SnapshotPolicyModel struct {
59+
Id types.String `tfsdk:"id"`
60+
Name types.String `tfsdk:"name"`
61+
}
62+
63+
var snapshotPolicyTypes = map[string]attr.Type{
64+
"id": basetypes.StringType{},
65+
"name": basetypes.StringType{},
66+
}
67+
5468
// NewResourcePoolResource is a helper function to simplify the provider implementation.
5569
func NewResourcePoolResource() resource.Resource {
5670
return &resourcePoolResource{}
@@ -200,6 +214,26 @@ func (r *resourcePoolResource) Schema(_ context.Context, _ resource.SchemaReques
200214
Computed: true,
201215
Default: booldefault.StaticBool(false),
202216
},
217+
"snapshot_policy": schema.SingleNestedAttribute{
218+
Description: `Name of the snapshot policy.`,
219+
Optional: true,
220+
Attributes: map[string]schema.Attribute{
221+
"id": schema.StringAttribute{
222+
Description: "ID of the snapshot policy.",
223+
Required: true, // must be set when snapshot_policy != null
224+
Validators: []validator.String{
225+
validate.UUID(),
226+
},
227+
},
228+
"name": schema.StringAttribute{
229+
Description: "Name of the snapshot policy.",
230+
Computed: true,
231+
PlanModifiers: []planmodifier.String{
232+
stringplanmodifierUtils.UseStateForUnknownIf(stringplanmodifierUtils.StringChanged, "id", "sets `UseStateForUnknown` only if `id` has not changed"),
233+
},
234+
},
235+
},
236+
},
203237
},
204238
}
205239
}
@@ -221,7 +255,7 @@ func (r *resourcePoolResource) Create(ctx context.Context, req resource.CreateRe
221255

222256
ctx = core.InitProviderContext(ctx)
223257

224-
payload, err := toCreatePayload(&model)
258+
payload, err := toCreatePayload(ctx, &model)
225259
if err != nil {
226260
core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating resource pool", fmt.Sprintf("Cannot create payload: %v", err))
227261
return
@@ -314,9 +348,8 @@ func (r *resourcePoolResource) Read(ctx context.Context, req resource.ReadReques
314348

315349
response, err := r.client.DefaultAPI.GetResourcePool(ctx, projectId, region, resourcePoolId).Execute()
316350
if err != nil {
317-
var openapiError *oapierror.GenericOpenAPIError
318-
if errors.As(err, &openapiError) {
319-
if openapiError.StatusCode == http.StatusNotFound {
351+
if openapiError, ok := errors.AsType[*oapierror.GenericOpenAPIError](err); ok {
352+
if openapiError.StatusCode == http.StatusNotFound || openapiError.StatusCode == http.StatusGone {
320353
resp.State.RemoveResource(ctx)
321354
return
322355
}
@@ -368,7 +401,7 @@ func (r *resourcePoolResource) Update(ctx context.Context, req resource.UpdateRe
368401
return
369402
}
370403

371-
payload, err := toUpdatePayload(&model)
404+
payload, err := toUpdatePayload(ctx, &model)
372405
if err != nil {
373406
core.LogAndAddError(ctx, &resp.Diagnostics, "Update resource pool", fmt.Sprintf("cannot create payload: %v", err))
374407
return
@@ -520,10 +553,22 @@ func mapFields(ctx context.Context, region string, resourcePool *sfs.ResourcePoo
520553
model.SizeGigabytes = types.Int32PointerValue(resourcePool.Space.SizeGigabytes)
521554
}
522555

556+
model.SnapshotPolicy = types.ObjectNull(snapshotPolicyTypes)
557+
if snapshotPolicy := resourcePool.SnapshotPolicy.Get(); snapshotPolicy != nil {
558+
snapshotPolicyTf, diags := types.ObjectValue(snapshotPolicyTypes, map[string]attr.Value{
559+
"id": types.StringPointerValue(snapshotPolicy.Id),
560+
"name": types.StringPointerValue(snapshotPolicy.Name),
561+
})
562+
if diags.HasError() {
563+
return fmt.Errorf("failed to map snapshot policy: %w", core.DiagsToError(diags))
564+
}
565+
model.SnapshotPolicy = snapshotPolicyTf
566+
}
567+
523568
return nil
524569
}
525570

526-
func toCreatePayload(model *Model) (*sfs.CreateResourcePoolPayload, error) {
571+
func toCreatePayload(ctx context.Context, model *Model) (*sfs.CreateResourcePoolPayload, error) {
527572
if model == nil {
528573
return nil, fmt.Errorf("nil model")
529574
}
@@ -538,18 +583,28 @@ func toCreatePayload(model *Model) (*sfs.CreateResourcePoolPayload, error) {
538583
aclList = tmp
539584
}
540585

586+
snapshotPolicy := &SnapshotPolicyModel{}
587+
if !utils.IsUndefined(model.SnapshotPolicy) {
588+
diags := model.SnapshotPolicy.As(ctx, snapshotPolicy, basetypes.ObjectAsOptions{})
589+
if diags.HasError() {
590+
return nil, fmt.Errorf("cannot convert snapshot policy: %w", core.DiagsToError(diags))
591+
}
592+
}
593+
541594
result := &sfs.CreateResourcePoolPayload{
542595
AvailabilityZone: model.AvailabilityZone.ValueString(),
543596
IpAcl: aclList,
544597
Name: model.Name.ValueString(),
545598
PerformanceClass: model.PerformanceClass.ValueString(),
546599
SizeGigabytes: model.SizeGigabytes.ValueInt32(),
547600
SnapshotsAreVisible: model.SnapshotsAreVisible.ValueBoolPointer(),
601+
SnapshotPolicyId: snapshotPolicy.Id.ValueStringPointer(),
548602
}
603+
549604
return result, nil
550605
}
551606

552-
func toUpdatePayload(model *Model) (*sfs.UpdateResourcePoolPayload, error) {
607+
func toUpdatePayload(ctx context.Context, model *Model) (*sfs.UpdateResourcePoolPayload, error) {
553608
if model == nil {
554609
return nil, fmt.Errorf("nil model")
555610
}
@@ -564,11 +619,20 @@ func toUpdatePayload(model *Model) (*sfs.UpdateResourcePoolPayload, error) {
564619
aclList = tmp
565620
}
566621

622+
snapshotPolicy := &SnapshotPolicyModel{}
623+
if !utils.IsUndefined(model.SnapshotPolicy) {
624+
diags := model.SnapshotPolicy.As(ctx, snapshotPolicy, basetypes.ObjectAsOptions{})
625+
if diags.HasError() {
626+
return nil, fmt.Errorf("cannot convert snapshot policy: %w", core.DiagsToError(diags))
627+
}
628+
}
629+
567630
result := &sfs.UpdateResourcePoolPayload{
568631
IpAcl: aclList,
569632
PerformanceClass: model.PerformanceClass.ValueStringPointer(),
570633
SizeGigabytes: *sfs.NewNullableInt32(model.SizeGigabytes.ValueInt32Pointer()),
571634
SnapshotsAreVisible: model.SnapshotsAreVisible.ValueBoolPointer(),
635+
SnapshotPolicyId: *sfs.NewNullableString(snapshotPolicy.Id.ValueStringPointer()),
572636
}
573637
return result, nil
574638
}

stackit/internal/services/sfs/resourcepool/resource_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,13 +135,18 @@ func TestToCreatePayload(t *testing.T) {
135135
Name: types.StringValue("testname"),
136136
PerformanceClass: types.StringValue("performance"),
137137
SizeGigabytes: types.Int32Value(42),
138+
SnapshotPolicy: types.ObjectValueMust(snapshotPolicyTypes, map[string]attr.Value{
139+
"id": types.StringValue("snapshot-id"),
140+
"name": types.StringNull(),
141+
}),
138142
},
139143
&sfs.CreateResourcePoolPayload{
140144
AvailabilityZone: testAvailabilityZone.ValueString(),
141145
IpAcl: []string{"foo", "bar", "baz"},
142146
Name: "testname",
143147
PerformanceClass: "performance",
144148
SizeGigabytes: 42,
149+
SnapshotPolicyId: new("snapshot-id"),
145150
},
146151
false,
147152
},
@@ -169,7 +174,8 @@ func TestToCreatePayload(t *testing.T) {
169174
}
170175
for _, tt := range tests {
171176
t.Run(tt.name, func(t *testing.T) {
172-
got, err := toCreatePayload(tt.model)
177+
ctx := context.Background()
178+
got, err := toCreatePayload(ctx, tt.model)
173179
if (err != nil) != tt.wantErr {
174180
t.Errorf("toCreatePayload() error = %v, wantErr %v", err, tt.wantErr)
175181
return
@@ -206,6 +212,7 @@ func TestToUpdatePayload(t *testing.T) {
206212
PerformanceClass: new("performance"),
207213
SizeGigabytes: *sfs.NewNullableInt32(utils.Ptr[int32](42)),
208214
SnapshotsAreVisible: new(true),
215+
SnapshotPolicyId: *sfs.NewNullableString(nil),
209216
},
210217
false,
211218
},
@@ -225,6 +232,7 @@ func TestToUpdatePayload(t *testing.T) {
225232
IpAcl: nil,
226233
PerformanceClass: new("performance"),
227234
SizeGigabytes: *sfs.NewNullableInt32(utils.Ptr[int32](42)),
235+
SnapshotPolicyId: *sfs.NewNullableString(nil),
228236
},
229237
false,
230238
},
@@ -244,13 +252,15 @@ func TestToUpdatePayload(t *testing.T) {
244252
IpAcl: []string{},
245253
PerformanceClass: new("performance"),
246254
SizeGigabytes: *sfs.NewNullableInt32(utils.Ptr[int32](42)),
255+
SnapshotPolicyId: *sfs.NewNullableString(nil),
247256
},
248257
false,
249258
},
250259
}
251260
for _, tt := range tests {
252261
t.Run(tt.name, func(t *testing.T) {
253-
got, err := toUpdatePayload(tt.model)
262+
ctx := context.Background()
263+
got, err := toUpdatePayload(ctx, tt.model)
254264
if (err != nil) != tt.wantErr {
255265
t.Errorf("toUpdatePayload() error = %v, wantErr %v", err, tt.wantErr)
256266
return

0 commit comments

Comments
 (0)