Skip to content

Commit 248b983

Browse files
hcsa73Henrique Santos
andauthored
Object storage misc fixes (#82)
* Fix wrong reference * Fix schema * Fix mapFields not fetching credentials group id * Change expiration timestamp * Fix schema * Remove fields that don't come in the GET response * Add RFC3339SecondsOnly * Change expiration timestamp to not support fractional seconds * Set retry timeout * Harmonize expiration timestamp * Skip import check on credential keys * Add error check * Update docs * Change field description * Add test case, simplify test * Add test case, simplify test * Rename variable * Generate docs --------- Co-authored-by: Henrique Santos <[email protected]>
1 parent 5a5ac66 commit 248b983

File tree

11 files changed

+229
-73
lines changed

11 files changed

+229
-73
lines changed

docs/data-sources/objectstorage_credential.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ ObjectStorage credential data source schema.
1919

2020
- `credential_id` (String) The credential ID.
2121
- `credentials_group_id` (String) The credential group ID.
22-
- `expiration_timestamp` (String)
2322
- `project_id` (String) STACKIT Project ID to which the credential group is associated.
2423

2524
### Read-Only
2625

2726
- `access_key` (String)
27+
- `expiration_timestamp` (String)
2828
- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`credentials_group_id`,`credential_id`".
2929
- `name` (String)
3030
- `secret_access_key` (String, Sensitive)

docs/resources/objectstorage_credential.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,17 @@ ObjectStorage credential resource schema.
1717

1818
### Required
1919

20+
- `credentials_group_id` (String) The credential group ID.
2021
- `project_id` (String) STACKIT Project ID to which the credential group is associated.
2122

2223
### Optional
2324

24-
- `expiration_timestamp` (String)
25+
- `expiration_timestamp` (String) Expiration timestamp, in RFC339 format without fractional seconds. Example: "2025-01-01T00:00:00Z". If not set, the credential never expires.
2526

2627
### Read-Only
2728

2829
- `access_key` (String)
2930
- `credential_id` (String) The credential ID.
30-
- `credentials_group_id` (String) The credential group ID.
3131
- `id` (String) Terraform's internal resource identifier. It is structured as "`project_id`,`credentials_group_id`,`credential_id`".
3232
- `name` (String)
3333
- `secret_access_key` (String, Sensitive)

go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ require (
66
github.com/google/go-cmp v0.6.0
77
github.com/google/uuid v1.3.1
88
github.com/hashicorp/terraform-plugin-framework v1.4.1
9-
github.com/hashicorp/terraform-plugin-framework-timetypes v0.3.0
109
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0
1110
github.com/hashicorp/terraform-plugin-go v0.19.0
1211
github.com/hashicorp/terraform-plugin-log v0.9.0

go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,6 @@ github.com/hashicorp/terraform-json v0.17.1 h1:eMfvh/uWggKmY7Pmb3T85u86E2EQg6EQH
7474
github.com/hashicorp/terraform-json v0.17.1/go.mod h1:Huy6zt6euxaY9knPAFKjUITn8QxUFIe9VuSzb4zn/0o=
7575
github.com/hashicorp/terraform-plugin-framework v1.4.1 h1:ZC29MoB3Nbov6axHdgPbMz7799pT5H8kIrM8YAsaVrs=
7676
github.com/hashicorp/terraform-plugin-framework v1.4.1/go.mod h1:XC0hPcQbBvlbxwmjxuV/8sn8SbZRg4XwGMs22f+kqV0=
77-
github.com/hashicorp/terraform-plugin-framework-timetypes v0.3.0 h1:egR4InfakWkgepZNUATWGwkrPhaAYOTEybPfEol+G/I=
78-
github.com/hashicorp/terraform-plugin-framework-timetypes v0.3.0/go.mod h1:9vjvl36aY1p6KltaA5QCvGC5hdE/9t4YuhGftw6WOgE=
7977
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0 h1:HOjBuMbOEzl7snOdOoUfE2Jgeto6JOjLVQ39Ls2nksc=
8078
github.com/hashicorp/terraform-plugin-framework-validators v0.12.0/go.mod h1:jfHGE/gzjxYz6XoUwi/aYiiKrJDeutQNUtGQXkaHklg=
8179
github.com/hashicorp/terraform-plugin-go v0.19.0 h1:BuZx/6Cp+lkmiG0cOBk6Zps0Cb2tmqQpDM3iAtnhDQU=

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

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/hashicorp/terraform-plugin-log/tflog"
99
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
1010

11-
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
1211
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
1312
"github.com/stackitcloud/stackit-sdk-go/core/config"
1413
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
@@ -110,8 +109,7 @@ func (r *credentialDataSource) Schema(_ context.Context, _ datasource.SchemaRequ
110109
Sensitive: true,
111110
},
112111
"expiration_timestamp": schema.StringAttribute{
113-
CustomType: timetypes.RFC3339Type{},
114-
Required: true,
112+
Computed: true,
115113
},
116114
},
117115
}

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

Lines changed: 46 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,13 @@ import (
44
"context"
55
"fmt"
66
"strings"
7+
"time"
78

8-
"github.com/hashicorp/terraform-plugin-framework/diag"
99
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1010
"github.com/hashicorp/terraform-plugin-log/tflog"
1111
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
1212
"github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate"
1313

14-
"github.com/hashicorp/terraform-plugin-framework-timetypes/timetypes"
1514
"github.com/hashicorp/terraform-plugin-framework/path"
1615
"github.com/hashicorp/terraform-plugin-framework/resource"
1716
"github.com/hashicorp/terraform-plugin-framework/resource/schema"
@@ -30,14 +29,14 @@ var (
3029
)
3130

3231
type Model struct {
33-
Id types.String `tfsdk:"id"` // needed by TF
34-
CredentialId types.String `tfsdk:"credential_id"`
35-
CredentialsGroupId types.String `tfsdk:"credentials_group_id"`
36-
ProjectId types.String `tfsdk:"project_id"`
37-
Name types.String `tfsdk:"name"`
38-
AccessKey types.String `tfsdk:"access_key"`
39-
SecretAccessKey types.String `tfsdk:"secret_access_key"`
40-
ExpirationTimestamp timetypes.RFC3339 `tfsdk:"expiration_timestamp"`
32+
Id types.String `tfsdk:"id"` // needed by TF
33+
CredentialId types.String `tfsdk:"credential_id"`
34+
CredentialsGroupId types.String `tfsdk:"credentials_group_id"`
35+
ProjectId types.String `tfsdk:"project_id"`
36+
Name types.String `tfsdk:"name"`
37+
AccessKey types.String `tfsdk:"access_key"`
38+
SecretAccessKey types.String `tfsdk:"secret_access_key"`
39+
ExpirationTimestamp types.String `tfsdk:"expiration_timestamp"`
4140
}
4241

4342
// NewCredentialResource is a helper function to simplify the provider implementation.
@@ -99,6 +98,7 @@ func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest,
9998
"credential_id": "The credential ID.",
10099
"credentials_group_id": "The credential group ID.",
101100
"project_id": "STACKIT Project ID to which the credential group is associated.",
101+
"expiration_timestamp": "Expiration timestamp, in RFC339 format without fractional seconds. Example: \"2025-01-01T00:00:00Z\". If not set, the credential never expires.",
102102
}
103103

104104
resp.Schema = schema.Schema{
@@ -124,7 +124,7 @@ func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest,
124124
},
125125
"credentials_group_id": schema.StringAttribute{
126126
Description: descriptions["credentials_group_id"],
127-
Computed: true,
127+
Required: true,
128128
PlanModifiers: []planmodifier.String{
129129
stringplanmodifier.UseStateForUnknown(),
130130
},
@@ -156,9 +156,12 @@ func (r *credentialResource) Schema(_ context.Context, _ resource.SchemaRequest,
156156
Sensitive: true,
157157
},
158158
"expiration_timestamp": schema.StringAttribute{
159-
CustomType: timetypes.RFC3339Type{},
160-
Optional: true,
161-
Computed: true,
159+
Description: descriptions["expiration_timestamp"],
160+
Optional: true,
161+
Computed: true,
162+
Validators: []validator.String{
163+
validate.RFC3339SecondsOnly(),
164+
},
162165
PlanModifiers: []planmodifier.String{
163166
stringplanmodifier.UseStateForUnknown(),
164167
},
@@ -325,9 +328,13 @@ func toCreatePayload(model *Model) (*objectstorage.CreateAccessKeyPayload, error
325328
return &objectstorage.CreateAccessKeyPayload{}, nil
326329
}
327330

328-
expirationTimestamp, diags := model.ExpirationTimestamp.ValueRFC3339Time()
329-
if diags.HasError() {
330-
return nil, fmt.Errorf("unable to fecth expiration timestamp: %w", core.DiagsToError(diags))
331+
expirationTimestampValue := model.ExpirationTimestamp.ValueStringPointer()
332+
if expirationTimestampValue == nil {
333+
return &objectstorage.CreateAccessKeyPayload{}, nil
334+
}
335+
expirationTimestamp, err := time.Parse(time.RFC3339, *expirationTimestampValue)
336+
if err != nil {
337+
return nil, fmt.Errorf("unable to parse expiration timestamp '%v': %w", *expirationTimestampValue, err)
331338
}
332339
return &objectstorage.CreateAccessKeyPayload{
333340
Expires: &expirationTimestamp,
@@ -351,7 +358,17 @@ func mapFields(credentialResp *objectstorage.CreateAccessKeyResponse, model *Mod
351358
return fmt.Errorf("credential id not present")
352359
}
353360

354-
var diags diag.Diagnostics
361+
if credentialResp.Expires == nil {
362+
model.ExpirationTimestamp = types.StringNull()
363+
} else {
364+
// Harmonize the timestamp format
365+
// Eg. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z"
366+
expirationTimestamp, err := time.Parse(time.RFC3339, *credentialResp.Expires)
367+
if err != nil {
368+
return fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", *credentialResp.Expires, err)
369+
}
370+
model.ExpirationTimestamp = types.StringValue(expirationTimestamp.Format(time.RFC3339))
371+
}
355372

356373
idParts := []string{
357374
model.ProjectId.ValueString(),
@@ -365,10 +382,6 @@ func mapFields(credentialResp *objectstorage.CreateAccessKeyResponse, model *Mod
365382
model.Name = types.StringPointerValue(credentialResp.DisplayName)
366383
model.AccessKey = types.StringPointerValue(credentialResp.AccessKey)
367384
model.SecretAccessKey = types.StringPointerValue(credentialResp.SecretAccessKey)
368-
model.ExpirationTimestamp, diags = timetypes.NewRFC3339PointerValue(credentialResp.Expires)
369-
if diags.HasError() {
370-
return fmt.Errorf("parsing expiration timestamp: %w", core.DiagsToError(diags))
371-
}
372385
return nil
373386
}
374387

@@ -396,8 +409,6 @@ func readCredentials(ctx context.Context, model *Model, client *objectstorage.AP
396409

397410
foundCredential = true
398411

399-
var diags diag.Diagnostics
400-
401412
idParts := []string{
402413
projectId,
403414
credentialsGroupId,
@@ -407,9 +418,17 @@ func readCredentials(ctx context.Context, model *Model, client *objectstorage.AP
407418
strings.Join(idParts, core.Separator),
408419
)
409420
model.Name = types.StringPointerValue(credential.DisplayName)
410-
model.ExpirationTimestamp, diags = timetypes.NewRFC3339PointerValue(credential.Expires)
411-
if diags.HasError() {
412-
return fmt.Errorf("parsing expiration timestamp: %w", core.DiagsToError(diags))
421+
422+
if credential.Expires == nil {
423+
model.ExpirationTimestamp = types.StringNull()
424+
} else {
425+
// Harmonize the timestamp format
426+
// Eg. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z"
427+
expirationTimestamp, err := time.Parse(time.RFC3339, *credential.Expires)
428+
if err != nil {
429+
return fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", *credential.Expires, err)
430+
}
431+
model.ExpirationTimestamp = types.StringValue(expirationTimestamp.Format(time.RFC3339))
413432
}
414433
break
415434
}

0 commit comments

Comments
 (0)