@@ -3,14 +3,19 @@ package objectstorage
33import (
44 "context"
55 "fmt"
6+ "net/http"
7+ "strings"
8+ "time"
69
710 "github.com/hashicorp/terraform-plugin-framework/datasource"
11+ "github.com/hashicorp/terraform-plugin-framework/types"
812 "github.com/hashicorp/terraform-plugin-log/tflog"
913 "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core"
1014 "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils"
1115
1216 "github.com/hashicorp/terraform-plugin-framework/datasource/schema"
1317 "github.com/stackitcloud/stackit-sdk-go/core/config"
18+ "github.com/stackitcloud/stackit-sdk-go/core/oapierror"
1419 "github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
1520)
1621
1924 _ datasource.DataSource = & credentialDataSource {}
2025)
2126
27+ type DataSourceModel struct {
28+ Id types.String `tfsdk:"id"` // needed by TF
29+ CredentialId types.String `tfsdk:"credential_id"`
30+ CredentialsGroupId types.String `tfsdk:"credentials_group_id"`
31+ ProjectId types.String `tfsdk:"project_id"`
32+ Name types.String `tfsdk:"name"`
33+ ExpirationTimestamp types.String `tfsdk:"expiration_timestamp"`
34+ Region types.String `tfsdk:"region"`
35+ }
36+
2237// NewCredentialDataSource is a helper function to simplify the provider implementation.
2338func NewCredentialDataSource () datasource.DataSource {
2439 return & credentialDataSource {}
@@ -104,13 +119,6 @@ func (r *credentialDataSource) Schema(_ context.Context, _ datasource.SchemaRequ
104119 "name" : schema.StringAttribute {
105120 Computed : true ,
106121 },
107- "access_key" : schema.StringAttribute {
108- Computed : true ,
109- },
110- "secret_access_key" : schema.StringAttribute {
111- Computed : true ,
112- Sensitive : true ,
113- },
114122 "expiration_timestamp" : schema.StringAttribute {
115123 Computed : true ,
116124 },
@@ -125,7 +133,7 @@ func (r *credentialDataSource) Schema(_ context.Context, _ datasource.SchemaRequ
125133
126134// Read refreshes the Terraform state with the latest data.
127135func (r * credentialDataSource ) Read (ctx context.Context , req datasource.ReadRequest , resp * datasource.ReadResponse ) { // nolint:gocritic // function signature required by Terraform
128- var model Model
136+ var model DataSourceModel
129137 diags := req .Config .Get (ctx , & model )
130138 resp .Diagnostics .Append (diags ... )
131139 if resp .Diagnostics .HasError () {
@@ -147,17 +155,33 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
147155 ctx = tflog .SetField (ctx , "credential_id" , credentialId )
148156 ctx = tflog .SetField (ctx , "region" , region )
149157
150- found , err := readCredentials (ctx , & model , region , r . client )
158+ credentialsGroupResp , err := r . client . ListAccessKeys (ctx , projectId , region ). CredentialsGroup ( credentialsGroupId ). Execute ( )
151159 if err != nil {
152- core .LogAndAddError (ctx , & resp .Diagnostics , "Error reading credential" , fmt .Sprintf ("Finding credential: %v" , err ))
160+ oapiErr , ok := err .(* oapierror.GenericOpenAPIError ) //nolint:errorlint //complaining that error.As should be used to catch wrapped errors, but this error should not be wrapped
161+ if ok && oapiErr .StatusCode == http .StatusNotFound {
162+ resp .State .RemoveResource (ctx )
163+ return
164+ }
165+ core .LogAndAddError (ctx , & resp .Diagnostics , "Error reading credentials" , fmt .Sprintf ("Calling API: %v" , err ))
166+ return
167+ }
168+ if credentialsGroupResp == nil {
169+ core .LogAndAddError (ctx , & resp .Diagnostics , "Error reading credentials" , fmt .Sprintf ("Response is nil: %v" , err ))
153170 return
154171 }
155- if ! found {
156- resp .State .RemoveResource (ctx )
172+
173+ credential := findCredential (* credentialsGroupResp , credentialId )
174+ if credential == nil {
157175 core .LogAndAddError (ctx , & resp .Diagnostics , "Error reading credential" , "Credential not found" )
158176 return
159177 }
160178
179+ err = mapDataSourceFields (credential , & model , region )
180+ if err != nil {
181+ core .LogAndAddError (ctx , & resp .Diagnostics , "Error reading credential" , fmt .Sprintf ("Processing API payload: %v" , err ))
182+ return
183+ }
184+
161185 // Set refreshed state
162186 diags = resp .State .Set (ctx , model )
163187 resp .Diagnostics .Append (diags ... )
@@ -166,3 +190,57 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ
166190 }
167191 tflog .Info (ctx , "ObjectStorage credential read" )
168192}
193+
194+ func mapDataSourceFields (credentialResp * objectstorage.AccessKey , model * DataSourceModel , region string ) error {
195+ if credentialResp == nil {
196+ return fmt .Errorf ("response input is nil" )
197+ }
198+ if model == nil {
199+ return fmt .Errorf ("model input is nil" )
200+ }
201+
202+ var credentialId string
203+ if model .CredentialId .ValueString () != "" {
204+ credentialId = model .CredentialId .ValueString ()
205+ } else if credentialResp .KeyId != nil {
206+ credentialId = * credentialResp .KeyId
207+ } else {
208+ return fmt .Errorf ("credential id not present" )
209+ }
210+
211+ if credentialResp .Expires == nil {
212+ model .ExpirationTimestamp = types .StringNull ()
213+ } else {
214+ // Harmonize the timestamp format
215+ // Eg. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z"
216+ expirationTimestamp , err := time .Parse (time .RFC3339 , * credentialResp .Expires )
217+ if err != nil {
218+ return fmt .Errorf ("unable to parse payload expiration timestamp '%v': %w" , * credentialResp .Expires , err )
219+ }
220+ model .ExpirationTimestamp = types .StringValue (expirationTimestamp .Format (time .RFC3339 ))
221+ }
222+
223+ idParts := []string {
224+ model .ProjectId .ValueString (),
225+ model .CredentialsGroupId .ValueString (),
226+ credentialId ,
227+ }
228+ model .Id = types .StringValue (
229+ strings .Join (idParts , core .Separator ),
230+ )
231+ model .CredentialId = types .StringValue (credentialId )
232+ model .Name = types .StringPointerValue (credentialResp .DisplayName )
233+ model .Region = types .StringValue (region )
234+ return nil
235+ }
236+
237+ // Returns the access key if found otherwise nil
238+ func findCredential (credentialsGroupResp objectstorage.ListAccessKeysResponse , credentialId string ) * objectstorage.AccessKey {
239+ for _ , credential := range * credentialsGroupResp .AccessKeys {
240+ if credential .KeyId == nil || * credential .KeyId != credentialId {
241+ continue
242+ }
243+ return & credential
244+ }
245+ return nil
246+ }
0 commit comments