From 456d784234a28d0fc481797f02cea9ef0846aac0 Mon Sep 17 00:00:00 2001 From: Ruben Hoenle Date: Fri, 27 Mar 2026 17:21:05 +0100 Subject: [PATCH] chore(objecstorage): switch to new SDK structure relates to STACKITTPR-565 --- .../objectstorage/bucket/datasource.go | 4 +- .../services/objectstorage/bucket/resource.go | 33 ++--- .../objectstorage/bucket/resource_test.go | 53 ++++--- .../compliance-lock/datasource.go | 6 +- .../objectstorage/compliance-lock/resource.go | 16 +-- .../compliance-lock/resource_test.go | 15 +- .../objectstorage/credential/datasource.go | 22 ++- .../credential/datasource_test.go | 26 ++-- .../objectstorage/credential/resource.go | 55 ++++---- .../objectstorage/credential/resource_test.go | 131 +++++++++--------- .../credentialsgroup/datasource.go | 4 +- .../credentialsgroup/resource.go | 47 +++---- .../credentialsgroup/resource_test.go | 101 +++++++------- .../objectstorage/objectstorage_acc_test.go | 32 ++--- .../objectstorage/objectstorage_test.go | 11 +- .../services/objectstorage/utils/util.go | 2 +- .../services/objectstorage/utils/util_test.go | 2 +- 17 files changed, 268 insertions(+), 292 deletions(-) diff --git a/stackit/internal/services/objectstorage/bucket/datasource.go b/stackit/internal/services/objectstorage/bucket/datasource.go index cd2f513b1..267e8f306 100644 --- a/stackit/internal/services/objectstorage/bucket/datasource.go +++ b/stackit/internal/services/objectstorage/bucket/datasource.go @@ -16,7 +16,7 @@ import ( "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) // Ensure the implementation satisfies the expected interfaces. @@ -129,7 +129,7 @@ func (r *bucketDataSource) Read(ctx context.Context, req datasource.ReadRequest, ctx = tflog.SetField(ctx, "name", bucketName) ctx = tflog.SetField(ctx, "region", region) - bucketResp, err := r.client.GetBucket(ctx, projectId, region, bucketName).Execute() + bucketResp, err := r.client.DefaultAPI.GetBucket(ctx, projectId, region, bucketName).Execute() if err != nil { utils.LogError( ctx, diff --git a/stackit/internal/services/objectstorage/bucket/resource.go b/stackit/internal/services/objectstorage/bucket/resource.go index 1711c55cd..4954ca37c 100644 --- a/stackit/internal/services/objectstorage/bucket/resource.go +++ b/stackit/internal/services/objectstorage/bucket/resource.go @@ -24,8 +24,8 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/wait" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" + "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api/wait" ) // Ensure the implementation satisfies the expected interfaces. @@ -202,14 +202,14 @@ func (r *bucketResource) Create(ctx context.Context, req resource.CreateRequest, ctx = tflog.SetField(ctx, "region", region) // Handle project init - err := enableProject(ctx, &model, region, r.client) + err := enableProject(ctx, &model, region, r.client.DefaultAPI) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating bucket", fmt.Sprintf("Enabling object storage project before creation: %v", err)) return } // Create new bucket - _, err = r.client.CreateBucket(ctx, projectId, region, bucketName).ObjectLockEnabled(model.ObjectLock.ValueBool()).Execute() + _, err = r.client.DefaultAPI.CreateBucket(ctx, projectId, region, bucketName).ObjectLockEnabled(model.ObjectLock.ValueBool()).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating bucket", fmt.Sprintf("Calling API: %v", err)) return @@ -227,7 +227,7 @@ func (r *bucketResource) Create(ctx context.Context, req resource.CreateRequest, return } - waitResp, err := wait.CreateBucketWaitHandler(ctx, r.client, projectId, region, bucketName).WaitWithContext(ctx) + waitResp, err := wait.CreateBucketWaitHandler(ctx, r.client.DefaultAPI, projectId, region, bucketName).WaitWithContext(ctx) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating bucket", fmt.Sprintf("Bucket creation waiting: %v", err)) return @@ -266,7 +266,7 @@ func (r *bucketResource) Read(ctx context.Context, req resource.ReadRequest, res ctx = tflog.SetField(ctx, "name", bucketName) ctx = tflog.SetField(ctx, "region", region) - bucketResp, err := r.client.GetBucket(ctx, projectId, region, bucketName).Execute() + bucketResp, err := r.client.DefaultAPI.GetBucket(ctx, projectId, region, bucketName).Execute() if err != nil { 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 if ok && oapiErr.StatusCode == http.StatusNotFound { @@ -321,7 +321,7 @@ func (r *bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, ctx = tflog.SetField(ctx, "region", region) // Delete existing bucket - _, err := r.client.DeleteBucket(ctx, projectId, region, bucketName).Execute() + _, err := r.client.DefaultAPI.DeleteBucket(ctx, projectId, region, bucketName).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError if errors.As(err, &oapiErr) { @@ -335,7 +335,7 @@ func (r *bucketResource) Delete(ctx context.Context, req resource.DeleteRequest, ctx = core.LogResponse(ctx) - _, err = wait.DeleteBucketWaitHandler(ctx, r.client, projectId, region, bucketName).WaitWithContext(ctx) + _, err = wait.DeleteBucketWaitHandler(ctx, r.client.DefaultAPI, projectId, region, bucketName).WaitWithContext(ctx) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting bucket", fmt.Sprintf("Bucket deletion waiting: %v", err)) return @@ -368,32 +368,25 @@ func mapFields(bucketResp *objectstorage.GetBucketResponse, model *Model, region if bucketResp == nil { return fmt.Errorf("response input is nil") } - if bucketResp.Bucket == nil { - return fmt.Errorf("response bucket is nil") - } if model == nil { return fmt.Errorf("model input is nil") } bucket := bucketResp.Bucket model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, model.Name.ValueString()) - model.URLPathStyle = types.StringPointerValue(bucket.UrlPathStyle) - model.URLVirtualHostedStyle = types.StringPointerValue(bucket.UrlVirtualHostedStyle) + model.URLPathStyle = types.StringValue(bucket.UrlPathStyle) + model.URLVirtualHostedStyle = types.StringValue(bucket.UrlVirtualHostedStyle) model.Region = types.StringValue(region) - model.ObjectLock = types.BoolPointerValue(bucket.ObjectLockEnabled) + model.ObjectLock = types.BoolValue(bucket.ObjectLockEnabled) return nil } -type objectStorageClient interface { - EnableServiceExecute(ctx context.Context, projectId, region string) (*objectstorage.ProjectStatus, error) -} - // enableProject enables object storage for the specified project. If the project is already enabled, nothing happens -func enableProject(ctx context.Context, model *Model, region string, client objectStorageClient) error { +func enableProject(ctx context.Context, model *Model, region string, client objectstorage.DefaultAPI) error { projectId := model.ProjectId.ValueString() // From the object storage OAS: Creation will also be successful if the project is already enabled, but will not create a duplicate - _, err := client.EnableServiceExecute(ctx, projectId, region) + _, err := client.EnableService(ctx, projectId, region).Execute() if err != nil { return fmt.Errorf("failed to create object storage project: %w", err) } diff --git a/stackit/internal/services/objectstorage/bucket/resource_test.go b/stackit/internal/services/objectstorage/bucket/resource_test.go index 18aaa46c0..c388982dd 100644 --- a/stackit/internal/services/objectstorage/bucket/resource_test.go +++ b/stackit/internal/services/objectstorage/bucket/resource_test.go @@ -9,21 +9,23 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) -type objectStorageClientMocked struct { +type mockSettings struct { returnError bool } -func (c *objectStorageClientMocked) EnableServiceExecute(_ context.Context, projectId, _ string) (*objectstorage.ProjectStatus, error) { - if c.returnError { - return nil, fmt.Errorf("create project failed") - } +func newAPIMock(settings *mockSettings) objectstorage.DefaultAPI { + return &objectstorage.DefaultAPIServiceMock{ + EnableServiceExecuteMock: utils.Ptr(func(r objectstorage.ApiEnableServiceRequest) (*objectstorage.ProjectStatus, error) { + if settings.returnError { + return nil, fmt.Errorf("create project failed") + } - return &objectstorage.ProjectStatus{ - Project: utils.Ptr(projectId), - }, nil + return &objectstorage.ProjectStatus{}, nil + }), + } } func TestMapFields(t *testing.T) { @@ -38,25 +40,26 @@ func TestMapFields(t *testing.T) { { "default_values", &objectstorage.GetBucketResponse{ - Bucket: &objectstorage.Bucket{}, + Bucket: objectstorage.Bucket{}, }, Model{ Id: types.StringValue(id), Name: types.StringValue("bname"), ProjectId: types.StringValue("pid"), - URLPathStyle: types.StringNull(), - URLVirtualHostedStyle: types.StringNull(), + URLPathStyle: types.StringValue(""), + URLVirtualHostedStyle: types.StringValue(""), Region: types.StringValue("eu01"), + ObjectLock: types.BoolValue(false), }, true, }, { "simple_values", &objectstorage.GetBucketResponse{ - Bucket: &objectstorage.Bucket{ - UrlPathStyle: utils.Ptr("url/path/style"), - UrlVirtualHostedStyle: utils.Ptr("url/virtual/hosted/style"), - ObjectLockEnabled: utils.Ptr(true), + Bucket: objectstorage.Bucket{ + UrlPathStyle: "url/path/style", + UrlVirtualHostedStyle: "url/virtual/hosted/style", + ObjectLockEnabled: true, }, }, Model{ @@ -73,9 +76,9 @@ func TestMapFields(t *testing.T) { { "empty_strings", &objectstorage.GetBucketResponse{ - Bucket: &objectstorage.Bucket{ - UrlPathStyle: utils.Ptr(""), - UrlVirtualHostedStyle: utils.Ptr(""), + Bucket: objectstorage.Bucket{ + UrlPathStyle: "", + UrlVirtualHostedStyle: "", }, }, Model{ @@ -85,6 +88,7 @@ func TestMapFields(t *testing.T) { URLPathStyle: types.StringValue(""), URLVirtualHostedStyle: types.StringValue(""), Region: types.StringValue("eu01"), + ObjectLock: types.BoolValue(false), }, true, }, @@ -94,12 +98,6 @@ func TestMapFields(t *testing.T) { Model{}, false, }, - { - "no_bucket", - &objectstorage.GetBucketResponse{}, - Model{}, - false, - }, } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { @@ -143,9 +141,10 @@ func TestEnableProject(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - client := &objectStorageClientMocked{ + client := newAPIMock(&mockSettings{ returnError: tt.enableFails, - } + }) + err := enableProject(context.Background(), &Model{}, "eu01", client) if !tt.isValid && err == nil { t.Fatalf("Should have failed") diff --git a/stackit/internal/services/objectstorage/compliance-lock/datasource.go b/stackit/internal/services/objectstorage/compliance-lock/datasource.go index 06a6c2377..b1e4d554a 100644 --- a/stackit/internal/services/objectstorage/compliance-lock/datasource.go +++ b/stackit/internal/services/objectstorage/compliance-lock/datasource.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/datasource/schema" "github.com/hashicorp/terraform-plugin-framework/schema/validator" "github.com/hashicorp/terraform-plugin-log/tflog" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" objectstorageUtils "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/services/objectstorage/utils" @@ -79,7 +79,7 @@ func (d *compliancelockDataSource) Schema(_ context.Context, _ datasource.Schema validate.NoSeparator(), }, }, - "max_retention_days": schema.Int64Attribute{ + "max_retention_days": schema.Int32Attribute{ Description: descriptions["max_retention_days"], Computed: true, }, @@ -109,7 +109,7 @@ func (d *compliancelockDataSource) Read(ctx context.Context, req datasource.Read ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) - complianceResp, err := d.client.GetComplianceLock(ctx, projectId, region).Execute() + complianceResp, err := d.client.DefaultAPI.GetComplianceLock(ctx, projectId, region).Execute() if err != nil { utils.LogError( ctx, diff --git a/stackit/internal/services/objectstorage/compliance-lock/resource.go b/stackit/internal/services/objectstorage/compliance-lock/resource.go index 68c4d5fbf..18e8593f3 100644 --- a/stackit/internal/services/objectstorage/compliance-lock/resource.go +++ b/stackit/internal/services/objectstorage/compliance-lock/resource.go @@ -17,7 +17,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/hashicorp/terraform-plugin-log/tflog" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/conversion" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" @@ -33,7 +33,7 @@ type Model struct { Id types.String `tfsdk:"id"` // needed by TF ProjectId types.String `tfsdk:"project_id"` Region types.String `tfsdk:"region"` - MaxRetentionDays types.Int64 `tfsdk:"max_retention_days"` + MaxRetentionDays types.Int32 `tfsdk:"max_retention_days"` } // NewComplianceLockResource is a helper function to simplify the provider implementation. @@ -129,7 +129,7 @@ func (r *compliancelockResource) Schema(_ context.Context, _ resource.SchemaRequ validate.NoSeparator(), }, }, - "max_retention_days": schema.Int64Attribute{ + "max_retention_days": schema.Int32Attribute{ Description: descriptions["max_retention_days"], Computed: true, }, @@ -163,7 +163,7 @@ func (r *compliancelockResource) Create(ctx context.Context, req resource.Create ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) - complianceResp, err := r.client.CreateComplianceLock(ctx, projectId, region).Execute() + complianceResp, err := r.client.DefaultAPI.CreateComplianceLock(ctx, projectId, region).Execute() if err != nil { var oapiErr *oapierror.GenericOpenAPIError ok := errors.As(err, &oapiErr) @@ -174,7 +174,7 @@ func (r *compliancelockResource) Create(ctx context.Context, req resource.Create } tflog.Info(ctx, "Compliance lock is already enabled for this project. Please check duplicate resources.") - complianceResp, err = r.client.GetComplianceLock(ctx, projectId, region).Execute() + complianceResp, err = r.client.DefaultAPI.GetComplianceLock(ctx, projectId, region).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading compliance lock", fmt.Sprintf("Calling API: %v", err)) return @@ -212,7 +212,7 @@ func (r *compliancelockResource) Read(ctx context.Context, req resource.ReadRequ ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) - complianceResp, err := r.client.GetComplianceLock(ctx, projectId, region).Execute() + complianceResp, err := r.client.DefaultAPI.GetComplianceLock(ctx, projectId, region).Execute() if err != nil { 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 if ok && oapiErr.StatusCode == http.StatusNotFound { @@ -264,7 +264,7 @@ func (r *compliancelockResource) Delete(ctx context.Context, req resource.Delete ctx = tflog.SetField(ctx, "project_id", projectId) ctx = tflog.SetField(ctx, "region", region) - _, err := r.client.DeleteComplianceLock(ctx, projectId, region).Execute() + _, err := r.client.DefaultAPI.DeleteComplianceLock(ctx, projectId, region).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting compliance lock", fmt.Sprintf("Calling API: %v", err)) return @@ -285,6 +285,6 @@ func mapFields(complianceResp *objectstorage.ComplianceLockResponse, model *Mode model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region) model.Region = types.StringValue(region) - model.MaxRetentionDays = types.Int64PointerValue(complianceResp.MaxRetentionDays) + model.MaxRetentionDays = types.Int32Value(complianceResp.MaxRetentionDays) return nil } diff --git a/stackit/internal/services/objectstorage/compliance-lock/resource_test.go b/stackit/internal/services/objectstorage/compliance-lock/resource_test.go index 2cdb12a22..a81be4861 100644 --- a/stackit/internal/services/objectstorage/compliance-lock/resource_test.go +++ b/stackit/internal/services/objectstorage/compliance-lock/resource_test.go @@ -6,13 +6,13 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) func TestMapFields(t *testing.T) { const testRegion = "eu01" id := fmt.Sprintf("%s,%s", "pid", testRegion) - retentionDays := int64(30) + retentionDays := int32(30) tests := []struct { description string input *objectstorage.ComplianceLockResponse @@ -23,22 +23,23 @@ func TestMapFields(t *testing.T) { "default_values", &objectstorage.ComplianceLockResponse{}, Model{ - Id: types.StringValue(id), - ProjectId: types.StringValue("pid"), - Region: types.StringValue("eu01"), + Id: types.StringValue(id), + ProjectId: types.StringValue("pid"), + Region: types.StringValue("eu01"), + MaxRetentionDays: types.Int32Value(0), }, true, }, { "simple_values", &objectstorage.ComplianceLockResponse{ - MaxRetentionDays: &retentionDays, + MaxRetentionDays: retentionDays, }, Model{ Id: types.StringValue(id), ProjectId: types.StringValue("pid"), Region: types.StringValue("eu01"), - MaxRetentionDays: types.Int64Value(retentionDays), + MaxRetentionDays: types.Int32Value(retentionDays), }, true, }, diff --git a/stackit/internal/services/objectstorage/credential/datasource.go b/stackit/internal/services/objectstorage/credential/datasource.go index 27841de82..75a85b7f7 100644 --- a/stackit/internal/services/objectstorage/credential/datasource.go +++ b/stackit/internal/services/objectstorage/credential/datasource.go @@ -16,7 +16,7 @@ import ( "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) // Ensure the implementation satisfies the expected interfaces. @@ -132,7 +132,7 @@ func (r *credentialDataSource) Read(ctx context.Context, req datasource.ReadRequ ctx = tflog.SetField(ctx, "credential_id", credentialId) ctx = tflog.SetField(ctx, "region", region) - credentialsGroupResp, err := r.client.ListAccessKeys(ctx, projectId, region).CredentialsGroup(credentialsGroupId).Execute() + credentialsGroupResp, err := r.client.DefaultAPI.ListAccessKeys(ctx, projectId, region).CredentialsGroup(credentialsGroupId).Execute() if err != nil { utils.LogError( ctx, @@ -187,20 +187,18 @@ func mapDataSourceFields(credentialResp *objectstorage.AccessKey, model *DataSou var credentialId string if model.CredentialId.ValueString() != "" { credentialId = model.CredentialId.ValueString() - } else if credentialResp.KeyId != nil { - credentialId = *credentialResp.KeyId } else { - return fmt.Errorf("credential id not present") + credentialId = credentialResp.KeyId } - if credentialResp.Expires == nil { + if credentialResp.Expires == "" { model.ExpirationTimestamp = types.StringNull() } else { // Harmonize the timestamp format - // Eg. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z" - expirationTimestamp, err := time.Parse(time.RFC3339, *credentialResp.Expires) + // E.g. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z" + expirationTimestamp, err := time.Parse(time.RFC3339, credentialResp.Expires) if err != nil { - return fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", *credentialResp.Expires, err) + return fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", credentialResp.Expires, err) } model.ExpirationTimestamp = types.StringValue(expirationTimestamp.Format(time.RFC3339)) } @@ -209,15 +207,15 @@ func mapDataSourceFields(credentialResp *objectstorage.AccessKey, model *DataSou model.ProjectId.ValueString(), region, model.CredentialsGroupId.ValueString(), credentialId, ) model.CredentialId = types.StringValue(credentialId) - model.Name = types.StringPointerValue(credentialResp.DisplayName) + model.Name = types.StringValue(credentialResp.DisplayName) model.Region = types.StringValue(region) return nil } // Returns the access key if found otherwise nil func findCredential(credentialsGroupResp objectstorage.ListAccessKeysResponse, credentialId string) *objectstorage.AccessKey { - for _, credential := range *credentialsGroupResp.AccessKeys { - if credential.KeyId == nil || *credential.KeyId != credentialId { + for _, credential := range credentialsGroupResp.AccessKeys { + if credential.KeyId != credentialId { continue } return &credential diff --git a/stackit/internal/services/objectstorage/credential/datasource_test.go b/stackit/internal/services/objectstorage/credential/datasource_test.go index e6ba0539a..599b3a525 100644 --- a/stackit/internal/services/objectstorage/credential/datasource_test.go +++ b/stackit/internal/services/objectstorage/credential/datasource_test.go @@ -7,8 +7,7 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/types" - "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) func TestMapDatasourceFields(t *testing.T) { @@ -24,14 +23,16 @@ func TestMapDatasourceFields(t *testing.T) { }{ { "default_values", - &objectstorage.AccessKey{}, + &objectstorage.AccessKey{ + Expires: now.Format(time.RFC3339Nano), + }, DataSourceModel{ Id: types.StringValue(id), ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cgid"), CredentialId: types.StringValue("cid"), - Name: types.StringNull(), - ExpirationTimestamp: types.StringNull(), + Name: types.StringValue(""), + ExpirationTimestamp: types.StringValue(now.Format(time.RFC3339)), Region: types.StringValue("eu01"), }, true, @@ -39,8 +40,8 @@ func TestMapDatasourceFields(t *testing.T) { { "simple_values", &objectstorage.AccessKey{ - DisplayName: utils.Ptr("name"), - Expires: utils.Ptr(now.Format(time.RFC3339)), + DisplayName: "name", + Expires: now.Format(time.RFC3339), }, DataSourceModel{ Id: types.StringValue(id), @@ -56,7 +57,8 @@ func TestMapDatasourceFields(t *testing.T) { { "empty_strings", &objectstorage.AccessKey{ - DisplayName: utils.Ptr(""), + DisplayName: "", + Expires: now.Format(time.RFC3339), }, DataSourceModel{ Id: types.StringValue(id), @@ -64,7 +66,7 @@ func TestMapDatasourceFields(t *testing.T) { CredentialsGroupId: types.StringValue("cgid"), CredentialId: types.StringValue("cid"), Name: types.StringValue(""), - ExpirationTimestamp: types.StringNull(), + ExpirationTimestamp: types.StringValue(now.Format(time.RFC3339)), Region: types.StringValue("eu01"), }, true, @@ -72,14 +74,14 @@ func TestMapDatasourceFields(t *testing.T) { { "expiration_timestamp_with_fractional_seconds", &objectstorage.AccessKey{ - Expires: utils.Ptr(now.Format(time.RFC3339Nano)), + Expires: now.Format(time.RFC3339Nano), }, DataSourceModel{ Id: types.StringValue(id), ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cgid"), CredentialId: types.StringValue("cid"), - Name: types.StringNull(), + Name: types.StringValue(""), ExpirationTimestamp: types.StringValue(now.Format(time.RFC3339)), Region: types.StringValue("eu01"), }, @@ -94,7 +96,7 @@ func TestMapDatasourceFields(t *testing.T) { { "bad_time", &objectstorage.AccessKey{ - Expires: utils.Ptr("foo-bar"), + Expires: "foo-bar", }, DataSourceModel{}, false, diff --git a/stackit/internal/services/objectstorage/credential/resource.go b/stackit/internal/services/objectstorage/credential/resource.go index c12e120e1..f19271781 100644 --- a/stackit/internal/services/objectstorage/credential/resource.go +++ b/stackit/internal/services/objectstorage/credential/resource.go @@ -23,7 +23,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) // Ensure the implementation satisfies the expected interfaces. @@ -260,7 +260,7 @@ func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequ ctx = tflog.SetField(ctx, "region", region) // Handle project init - err := enableProject(ctx, &model, region, r.client) + err := enableProject(ctx, &model, region, r.client.DefaultAPI) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Enabling object storage project before creation: %v", err)) return @@ -273,7 +273,7 @@ func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequ return } // Create new credential - credentialResp, err := r.client.CreateAccessKey(ctx, projectId, region).CredentialsGroup(credentialsGroupId).CreateAccessKeyPayload(*payload).Execute() + credentialResp, err := r.client.DefaultAPI.CreateAccessKey(ctx, projectId, region).CredentialsGroup(credentialsGroupId).CreateAccessKeyPayload(*payload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", fmt.Sprintf("Calling API: %v", err)) return @@ -281,11 +281,7 @@ func (r *credentialResource) Create(ctx context.Context, req resource.CreateRequ ctx = core.LogResponse(ctx) - if credentialResp.KeyId == nil { - core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credential", "Got empty credential id") - return - } - credentialId := *credentialResp.KeyId + credentialId := credentialResp.KeyId // Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ "project_id": projectId, @@ -432,7 +428,7 @@ func (r *credentialResource) Delete(ctx context.Context, req resource.DeleteRequ ctx = tflog.SetField(ctx, "region", region) // Delete existing credential - _, err := r.client.DeleteAccessKey(ctx, projectId, region, credentialId).CredentialsGroup(credentialsGroupId).Execute() + _, err := r.client.DefaultAPI.DeleteAccessKey(ctx, projectId, region, credentialId).CredentialsGroup(credentialsGroupId).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credential", fmt.Sprintf("Calling API: %v", err)) } @@ -463,16 +459,12 @@ func (r *credentialResource) ImportState(ctx context.Context, req resource.Impor tflog.Info(ctx, "ObjectStorage credential state imported") } -type objectStorageClient interface { - EnableServiceExecute(ctx context.Context, projectId, region string) (*objectstorage.ProjectStatus, error) -} - // enableProject enables object storage for the specified project. If the project is already enabled, nothing happens -func enableProject(ctx context.Context, model *Model, region string, client objectStorageClient) error { +func enableProject(ctx context.Context, model *Model, region string, client objectstorage.DefaultAPI) error { projectId := model.ProjectId.ValueString() // From the object storage OAS: Creation will also be successful if the project is already enabled, but will not create a duplicate - _, err := client.EnableServiceExecute(ctx, projectId, region) + _, err := client.EnableService(ctx, projectId, region).Execute() if err != nil { return fmt.Errorf("failed to create object storage project: %w", err) } @@ -512,20 +504,20 @@ func mapFields(credentialResp *objectstorage.CreateAccessKeyResponse, model *Mod var credentialId string if model.CredentialId.ValueString() != "" { credentialId = model.CredentialId.ValueString() - } else if credentialResp.KeyId != nil { - credentialId = *credentialResp.KeyId + } else if credentialResp.KeyId != "" { + credentialId = credentialResp.KeyId } else { return fmt.Errorf("credential id not present") } - if credentialResp.Expires == nil { + if credentialResp.Expires.Get() == nil || *credentialResp.Expires.Get() == "" { model.ExpirationTimestamp = types.StringNull() } else { // Harmonize the timestamp format - // Eg. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z" + // E.g. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z" expirationTimestamp, err := time.Parse(time.RFC3339, *credentialResp.Expires.Get()) if err != nil { - return fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", *credentialResp.Expires, err) + return fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", *credentialResp.Expires.Get(), err) } model.ExpirationTimestamp = types.StringValue(expirationTimestamp.Format(time.RFC3339)) } @@ -534,9 +526,9 @@ func mapFields(credentialResp *objectstorage.CreateAccessKeyResponse, model *Mod model.ProjectId.ValueString(), region, model.CredentialsGroupId.ValueString(), credentialId, ) model.CredentialId = types.StringValue(credentialId) - model.Name = types.StringPointerValue(credentialResp.DisplayName) - model.AccessKey = types.StringPointerValue(credentialResp.AccessKey) - model.SecretAccessKey = types.StringPointerValue(credentialResp.SecretAccessKey) + model.Name = types.StringValue(credentialResp.DisplayName) + model.AccessKey = types.StringValue(credentialResp.AccessKey) + model.SecretAccessKey = types.StringValue(credentialResp.SecretAccessKey) model.Region = types.StringValue(region) return nil } @@ -549,7 +541,7 @@ func readCredentials(ctx context.Context, model *Model, region string, client *o credentialsGroupId := model.CredentialsGroupId.ValueString() credentialId := model.CredentialId.ValueString() - credentialsGroupResp, err := client.ListAccessKeys(ctx, projectId, region).CredentialsGroup(credentialsGroupId).Execute() + credentialsGroupResp, err := client.DefaultAPI.ListAccessKeys(ctx, projectId, region).CredentialsGroup(credentialsGroupId).Execute() if err != nil { 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 if ok && oapiErr.StatusCode == http.StatusNotFound { @@ -562,27 +554,28 @@ func readCredentials(ctx context.Context, model *Model, region string, client *o } foundCredential := false - for _, credential := range *credentialsGroupResp.AccessKeys { - if credential.KeyId == nil || *credential.KeyId != credentialId { + for _, credential := range credentialsGroupResp.AccessKeys { + if credential.KeyId != credentialId { continue } foundCredential = true model.Id = utils.BuildInternalTerraformId(projectId, region, credentialsGroupId, credentialId) - model.Name = types.StringPointerValue(credential.DisplayName) + model.Name = types.StringValue(credential.DisplayName) - if credential.Expires == nil { + if credential.Expires == "" { model.ExpirationTimestamp = types.StringNull() } else { // Harmonize the timestamp format - // Eg. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z" - expirationTimestamp, err := time.Parse(time.RFC3339, *credential.Expires) + // E.g. "2027-01-02T03:04:05.000Z" = "2027-01-02T03:04:05Z" + expirationTimestamp, err := time.Parse(time.RFC3339, credential.Expires) if err != nil { - return foundCredential, fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", *credential.Expires, err) + return foundCredential, fmt.Errorf("unable to parse payload expiration timestamp '%v': %w", credential.Expires, err) } model.ExpirationTimestamp = types.StringValue(expirationTimestamp.Format(time.RFC3339)) } + break } model.Region = types.StringValue(region) diff --git a/stackit/internal/services/objectstorage/credential/resource_test.go b/stackit/internal/services/objectstorage/credential/resource_test.go index 24746aa2f..42b699402 100644 --- a/stackit/internal/services/objectstorage/credential/resource_test.go +++ b/stackit/internal/services/objectstorage/credential/resource_test.go @@ -13,21 +13,23 @@ import ( "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) -type objectStorageClientMocked struct { +type mockSettings struct { returnError bool } -func (c *objectStorageClientMocked) EnableServiceExecute(_ context.Context, projectId, _ string) (*objectstorage.ProjectStatus, error) { - if c.returnError { - return nil, fmt.Errorf("create project failed") - } +func newAPIMock(settings *mockSettings) objectstorage.DefaultAPI { + return &objectstorage.DefaultAPIServiceMock{ + EnableServiceExecuteMock: utils.Ptr(func(r objectstorage.ApiEnableServiceRequest) (*objectstorage.ProjectStatus, error) { + if settings.returnError { + return nil, fmt.Errorf("create project failed") + } - return &objectstorage.ProjectStatus{ - Project: utils.Ptr(projectId), - }, nil + return &objectstorage.ProjectStatus{}, nil + }), + } } func TestMapFields(t *testing.T) { @@ -48,9 +50,9 @@ func TestMapFields(t *testing.T) { ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cgid"), CredentialId: types.StringValue("cid"), - Name: types.StringNull(), - AccessKey: types.StringNull(), - SecretAccessKey: types.StringNull(), + Name: types.StringValue(""), + AccessKey: types.StringValue(""), + SecretAccessKey: types.StringValue(""), ExpirationTimestamp: types.StringNull(), Region: types.StringValue("eu01"), }, @@ -59,10 +61,10 @@ func TestMapFields(t *testing.T) { { "simple_values", &objectstorage.CreateAccessKeyResponse{ - AccessKey: utils.Ptr("key"), - DisplayName: utils.Ptr("name"), - Expires: objectstorage.NewNullableString(utils.Ptr(now.Format(time.RFC3339))), - SecretAccessKey: utils.Ptr("secret-key"), + AccessKey: "key", + DisplayName: "name", + Expires: *objectstorage.NewNullableString(utils.Ptr(now.Format(time.RFC3339))), + SecretAccessKey: "secret-key", }, Model{ Id: types.StringValue(id), @@ -80,9 +82,9 @@ func TestMapFields(t *testing.T) { { "empty_strings", &objectstorage.CreateAccessKeyResponse{ - AccessKey: utils.Ptr(""), - DisplayName: utils.Ptr(""), - SecretAccessKey: utils.Ptr(""), + AccessKey: "", + DisplayName: "", + SecretAccessKey: "", }, Model{ Id: types.StringValue(id), @@ -100,15 +102,16 @@ func TestMapFields(t *testing.T) { { "expiration_timestamp_with_fractional_seconds", &objectstorage.CreateAccessKeyResponse{ - Expires: objectstorage.NewNullableString(utils.Ptr(now.Format(time.RFC3339Nano))), + Expires: *objectstorage.NewNullableString(utils.Ptr(now.Format(time.RFC3339Nano))), }, Model{ Id: types.StringValue(id), ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cgid"), CredentialId: types.StringValue("cid"), - Name: types.StringNull(), - AccessKey: types.StringNull(), + Name: types.StringValue(""), + AccessKey: types.StringValue(""), + SecretAccessKey: types.StringValue(""), ExpirationTimestamp: types.StringValue(now.Format(time.RFC3339)), Region: types.StringValue("eu01"), }, @@ -123,7 +126,7 @@ func TestMapFields(t *testing.T) { { "bad_time", &objectstorage.CreateAccessKeyResponse{ - Expires: objectstorage.NewNullableString(utils.Ptr("foo-bar")), + Expires: *objectstorage.NewNullableString(utils.Ptr("foo-bar")), }, Model{}, false, @@ -195,9 +198,10 @@ func TestEnableProject(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - client := &objectStorageClientMocked{ + client := newAPIMock(&mockSettings{ returnError: tt.enableFails, - } + }) + model := &Model{ ProjectId: tt.expected.ProjectId, CredentialsGroupId: tt.expected.CredentialsGroupId, @@ -229,15 +233,16 @@ func TestReadCredentials(t *testing.T) { { "default_values", &objectstorage.ListAccessKeysResponse{ - AccessKeys: &[]objectstorage.AccessKey{ + AccessKeys: []objectstorage.AccessKey{ { - KeyId: utils.Ptr("foo-cid"), + KeyId: "foo-cid", }, { - KeyId: utils.Ptr("bar-cid"), + KeyId: "bar-cid", }, { - KeyId: utils.Ptr("cid"), + KeyId: "cid", + Expires: now.Format(time.RFC3339Nano), }, }, }, @@ -246,10 +251,10 @@ func TestReadCredentials(t *testing.T) { ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cgid"), CredentialId: types.StringValue("cid"), - Name: types.StringNull(), + Name: types.StringValue(""), AccessKey: types.StringNull(), SecretAccessKey: types.StringNull(), - ExpirationTimestamp: types.StringNull(), + ExpirationTimestamp: types.StringValue(now.Format(time.RFC3339)), Region: types.StringValue("eu01"), }, true, @@ -259,21 +264,21 @@ func TestReadCredentials(t *testing.T) { { "simple_values", &objectstorage.ListAccessKeysResponse{ - AccessKeys: &[]objectstorage.AccessKey{ + AccessKeys: []objectstorage.AccessKey{ { - KeyId: utils.Ptr("foo-cid"), - DisplayName: utils.Ptr("foo-name"), - Expires: utils.Ptr(now.Add(time.Hour).Format(time.RFC3339)), + KeyId: "foo-cid", + DisplayName: "foo-name", + Expires: now.Add(time.Hour).Format(time.RFC3339), }, { - KeyId: utils.Ptr("bar-cid"), - DisplayName: utils.Ptr("bar-name"), - Expires: utils.Ptr(now.Add(time.Minute).Format(time.RFC3339)), + KeyId: "bar-cid", + DisplayName: "bar-name", + Expires: now.Add(time.Minute).Format(time.RFC3339), }, { - KeyId: utils.Ptr("cid"), - DisplayName: utils.Ptr("name"), - Expires: utils.Ptr(now.Format(time.RFC3339)), + KeyId: "cid", + DisplayName: "name", + Expires: now.Format(time.RFC3339), }, }, }, @@ -295,21 +300,21 @@ func TestReadCredentials(t *testing.T) { { "expiration_timestamp_with_fractional_seconds", &objectstorage.ListAccessKeysResponse{ - AccessKeys: &[]objectstorage.AccessKey{ + AccessKeys: []objectstorage.AccessKey{ { - KeyId: utils.Ptr("foo-cid"), - DisplayName: utils.Ptr("foo-name"), - Expires: utils.Ptr(now.Add(time.Hour).Format(time.RFC3339Nano)), + KeyId: "foo-cid", + DisplayName: "foo-name", + Expires: now.Add(time.Hour).Format(time.RFC3339Nano), }, { - KeyId: utils.Ptr("bar-cid"), - DisplayName: utils.Ptr("bar-name"), - Expires: utils.Ptr(now.Add(time.Minute).Format(time.RFC3339Nano)), + KeyId: "bar-cid", + DisplayName: "bar-name", + Expires: now.Add(time.Minute).Format(time.RFC3339Nano), }, { - KeyId: utils.Ptr("cid"), - DisplayName: utils.Ptr("name"), - Expires: utils.Ptr(now.Format(time.RFC3339Nano)), + KeyId: "cid", + DisplayName: "name", + Expires: now.Format(time.RFC3339Nano), }, }, }, @@ -331,7 +336,7 @@ func TestReadCredentials(t *testing.T) { { "empty_credentials", &objectstorage.ListAccessKeysResponse{ - AccessKeys: &[]objectstorage.AccessKey{}, + AccessKeys: []objectstorage.AccessKey{}, }, Model{ Region: types.StringValue("eu01"), @@ -351,16 +356,16 @@ func TestReadCredentials(t *testing.T) { { "non_matching_credential", &objectstorage.ListAccessKeysResponse{ - AccessKeys: &[]objectstorage.AccessKey{ + AccessKeys: []objectstorage.AccessKey{ { - KeyId: utils.Ptr("foo-cid"), - DisplayName: utils.Ptr("foo-name"), - Expires: utils.Ptr(now.Add(time.Hour).Format(time.RFC3339)), + KeyId: "foo-cid", + DisplayName: "foo-name", + Expires: now.Add(time.Hour).Format(time.RFC3339), }, { - KeyId: utils.Ptr("bar-cid"), - DisplayName: utils.Ptr("bar-name"), - Expires: utils.Ptr(now.Add(time.Minute).Format(time.RFC3339)), + KeyId: "bar-cid", + DisplayName: "bar-name", + Expires: now.Add(time.Minute).Format(time.RFC3339), }, }, }, @@ -374,11 +379,11 @@ func TestReadCredentials(t *testing.T) { { "error_response", &objectstorage.ListAccessKeysResponse{ - AccessKeys: &[]objectstorage.AccessKey{ + AccessKeys: []objectstorage.AccessKey{ { - KeyId: utils.Ptr("cid"), - DisplayName: utils.Ptr("name"), - Expires: utils.Ptr(now.Format(time.RFC3339)), + KeyId: "cid", + DisplayName: "name", + Expires: now.Format(time.RFC3339), }, }, }, diff --git a/stackit/internal/services/objectstorage/credentialsgroup/datasource.go b/stackit/internal/services/objectstorage/credentialsgroup/datasource.go index de690b5da..d5b5f8afd 100644 --- a/stackit/internal/services/objectstorage/credentialsgroup/datasource.go +++ b/stackit/internal/services/objectstorage/credentialsgroup/datasource.go @@ -15,7 +15,7 @@ import ( "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/validate" "github.com/hashicorp/terraform-plugin-framework/datasource/schema" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) // Ensure the implementation satisfies the expected interfaces. @@ -122,7 +122,7 @@ func (r *credentialsGroupDataSource) Read(ctx context.Context, req datasource.Re ctx = tflog.SetField(ctx, "credentials_group_id", credentialsGroupId) ctx = tflog.SetField(ctx, "region", region) - found, err := readCredentialsGroups(ctx, &model, region, r.client) + found, err := readCredentialsGroups(ctx, &model, region, r.client.DefaultAPI) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credentials group", fmt.Sprintf("getting credential group from list of credentials groups: %v", err)) return diff --git a/stackit/internal/services/objectstorage/credentialsgroup/resource.go b/stackit/internal/services/objectstorage/credentialsgroup/resource.go index 49a8a3d76..51732274e 100644 --- a/stackit/internal/services/objectstorage/credentialsgroup/resource.go +++ b/stackit/internal/services/objectstorage/credentialsgroup/resource.go @@ -21,8 +21,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/resource/schema/stringplanmodifier" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" - sdkUtils "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) // Ensure the implementation satisfies the expected interfaces. @@ -187,18 +186,18 @@ func (r *credentialsGroupResource) Create(ctx context.Context, req resource.Crea ctx = tflog.SetField(ctx, "region", region) createCredentialsGroupPayload := objectstorage.CreateCredentialsGroupPayload{ - DisplayName: sdkUtils.Ptr(credentialsGroupName), + DisplayName: credentialsGroupName, } // Handle project init - err := enableProject(ctx, &model, region, r.client) + err := enableProject(ctx, &model, region, r.client.DefaultAPI) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credentials group", fmt.Sprintf("Enabling object storage project before creation: %v", err)) return } // Create new credentials group - got, err := r.client.CreateCredentialsGroup(ctx, projectId, region).CreateCredentialsGroupPayload(createCredentialsGroupPayload).Execute() + got, err := r.client.DefaultAPI.CreateCredentialsGroup(ctx, projectId, region).CreateCredentialsGroupPayload(createCredentialsGroupPayload).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credentials group", fmt.Sprintf("Calling API: %v", err)) return @@ -206,11 +205,11 @@ func (r *credentialsGroupResource) Create(ctx context.Context, req resource.Crea ctx = core.LogResponse(ctx) - if got == nil || got.CredentialsGroup == nil || got.CredentialsGroup.CredentialsGroupId == nil { + if got == nil || got.CredentialsGroup.CredentialsGroupId == "" { core.LogAndAddError(ctx, &resp.Diagnostics, "Error creating credentials group", "Got empty credential group id id") return } - credentialsGroupId := *got.CredentialsGroup.CredentialsGroupId + credentialsGroupId := got.CredentialsGroup.CredentialsGroupId // Write id attributes to state before polling via the wait handler - just in case anything goes wrong during the wait handler ctx = utils.SetAndLogStateFields(ctx, &resp.Diagnostics, &resp.State, map[string]any{ "project_id": projectId, @@ -254,7 +253,7 @@ func (r *credentialsGroupResource) Read(ctx context.Context, req resource.ReadRe ctx = tflog.SetField(ctx, "credentials_group_id", credentialsGroupId) ctx = tflog.SetField(ctx, "region", region) - found, err := readCredentialsGroups(ctx, &model, region, r.client) + found, err := readCredentialsGroups(ctx, &model, region, r.client.DefaultAPI) if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error reading credentialsGroup", fmt.Sprintf("getting credential group from list of credentials groups: %v", err)) return @@ -304,7 +303,7 @@ func (r *credentialsGroupResource) Delete(ctx context.Context, req resource.Dele ctx = tflog.SetField(ctx, "region", region) // Delete existing credentials group - _, err := r.client.DeleteCredentialsGroup(ctx, projectId, region, credentialsGroupId).Execute() + _, err := r.client.DefaultAPI.DeleteCredentialsGroup(ctx, projectId, region, credentialsGroupId).Execute() if err != nil { core.LogAndAddError(ctx, &resp.Diagnostics, "Error deleting credentials group", fmt.Sprintf("Calling API: %v", err)) } @@ -338,15 +337,12 @@ func mapFields(credentialsGroupResp *objectstorage.CreateCredentialsGroupRespons if credentialsGroupResp == nil { return fmt.Errorf("response input is nil") } - if credentialsGroupResp.CredentialsGroup == nil { - return fmt.Errorf("response credentialsGroup is nil") - } if model == nil { return fmt.Errorf("model input is nil") } credentialsGroup := credentialsGroupResp.CredentialsGroup - err := mapCredentialsGroup(*credentialsGroup, model, region) + err := mapCredentialsGroup(credentialsGroup, model, region) if err != nil { return err } @@ -358,30 +354,25 @@ func mapCredentialsGroup(credentialsGroup objectstorage.CredentialsGroup, model var credentialsGroupId string if !utils.IsUndefined(model.CredentialsGroupId) { credentialsGroupId = model.CredentialsGroupId.ValueString() - } else if credentialsGroup.CredentialsGroupId != nil { - credentialsGroupId = *credentialsGroup.CredentialsGroupId + } else if credentialsGroup.CredentialsGroupId != "" { + credentialsGroupId = credentialsGroup.CredentialsGroupId } else { return fmt.Errorf("credential id not present") } model.Id = utils.BuildInternalTerraformId(model.ProjectId.ValueString(), region, credentialsGroupId) model.CredentialsGroupId = types.StringValue(credentialsGroupId) - model.URN = types.StringPointerValue(credentialsGroup.Urn) - model.Name = types.StringPointerValue(credentialsGroup.DisplayName) + model.URN = types.StringValue(credentialsGroup.Urn) + model.Name = types.StringValue(credentialsGroup.DisplayName) return nil } -type objectStorageClient interface { - EnableServiceExecute(ctx context.Context, projectId, region string) (*objectstorage.ProjectStatus, error) - ListCredentialsGroupsExecute(ctx context.Context, projectId, region string) (*objectstorage.ListCredentialsGroupsResponse, error) -} - // enableProject enables object storage for the specified project. If the project is already enabled, nothing happens -func enableProject(ctx context.Context, model *Model, region string, client objectStorageClient) error { +func enableProject(ctx context.Context, model *Model, region string, client objectstorage.DefaultAPI) error { projectId := model.ProjectId.ValueString() // From the object storage OAS: Creation will also be successful if the project is already enabled, but will not create a duplicate - _, err := client.EnableServiceExecute(ctx, projectId, region) + _, err := client.EnableService(ctx, projectId, region).Execute() if err != nil { return fmt.Errorf("failed to create object storage project: %w", err) } @@ -391,14 +382,14 @@ func enableProject(ctx context.Context, model *Model, region string, client obje // readCredentialsGroups gets all the existing credentials groups for the specified project, // finds the credentials group that is being read and updates the state. // Returns True if the credential was found, False otherwise. -func readCredentialsGroups(ctx context.Context, model *Model, region string, client objectStorageClient) (bool, error) { +func readCredentialsGroups(ctx context.Context, model *Model, region string, client objectstorage.DefaultAPI) (bool, error) { found := false if model.CredentialsGroupId.ValueString() == "" && model.Name.ValueString() == "" { return found, fmt.Errorf("missing configuration: either name or credentials group id must be provided") } - credentialsGroupsResp, err := client.ListCredentialsGroupsExecute(ctx, model.ProjectId.ValueString(), region) + credentialsGroupsResp, err := client.ListCredentialsGroups(ctx, model.ProjectId.ValueString(), region).Execute() if err != nil { 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 if ok && oapiErr.StatusCode == http.StatusNotFound { @@ -411,8 +402,8 @@ func readCredentialsGroups(ctx context.Context, model *Model, region string, cli return found, fmt.Errorf("nil response from GET credentials groups") } - for _, credentialsGroup := range *credentialsGroupsResp.CredentialsGroups { - if *credentialsGroup.CredentialsGroupId != model.CredentialsGroupId.ValueString() && *credentialsGroup.DisplayName != model.Name.ValueString() { + for _, credentialsGroup := range credentialsGroupsResp.CredentialsGroups { + if credentialsGroup.CredentialsGroupId != model.CredentialsGroupId.ValueString() && credentialsGroup.DisplayName != model.Name.ValueString() { continue } found = true diff --git a/stackit/internal/services/objectstorage/credentialsgroup/resource_test.go b/stackit/internal/services/objectstorage/credentialsgroup/resource_test.go index 37b0dae84..991b244cd 100644 --- a/stackit/internal/services/objectstorage/credentialsgroup/resource_test.go +++ b/stackit/internal/services/objectstorage/credentialsgroup/resource_test.go @@ -8,30 +8,31 @@ import ( "github.com/google/go-cmp/cmp" "github.com/hashicorp/terraform-plugin-framework/types" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" ) -type objectStorageClientMocked struct { +type mockSettings struct { returnError bool listCredentialsGroupsResp *objectstorage.ListCredentialsGroupsResponse } -func (c *objectStorageClientMocked) EnableServiceExecute(_ context.Context, projectId, _ string) (*objectstorage.ProjectStatus, error) { - if c.returnError { - return nil, fmt.Errorf("create project failed") - } +func newAPIMock(settings *mockSettings) objectstorage.DefaultAPI { + return &objectstorage.DefaultAPIServiceMock{ + EnableServiceExecuteMock: utils.Ptr(func(_ objectstorage.ApiEnableServiceRequest) (*objectstorage.ProjectStatus, error) { + if settings.returnError { + return nil, fmt.Errorf("create project failed") + } - return &objectstorage.ProjectStatus{ - Project: utils.Ptr(projectId), - }, nil -} + return &objectstorage.ProjectStatus{}, nil + }), + ListCredentialsGroupsExecuteMock: utils.Ptr(func(_ objectstorage.ApiListCredentialsGroupsRequest) (*objectstorage.ListCredentialsGroupsResponse, error) { + if settings.returnError { + return nil, fmt.Errorf("get credentials groups failed") + } -func (c *objectStorageClientMocked) ListCredentialsGroupsExecute(_ context.Context, _, _ string) (*objectstorage.ListCredentialsGroupsResponse, error) { - if c.returnError { - return nil, fmt.Errorf("get credentials groups failed") + return settings.listCredentialsGroupsResp, nil + }), } - - return c.listCredentialsGroupsResp, nil } func TestMapFields(t *testing.T) { @@ -46,14 +47,14 @@ func TestMapFields(t *testing.T) { { "default_values", &objectstorage.CreateCredentialsGroupResponse{ - CredentialsGroup: &objectstorage.CredentialsGroup{}, + CredentialsGroup: objectstorage.CredentialsGroup{}, }, Model{ Id: types.StringValue(id), - Name: types.StringNull(), + Name: types.StringValue(""), ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cid"), - URN: types.StringNull(), + URN: types.StringValue(""), Region: types.StringValue("eu01"), }, true, @@ -61,9 +62,9 @@ func TestMapFields(t *testing.T) { { "simple_values", &objectstorage.CreateCredentialsGroupResponse{ - CredentialsGroup: &objectstorage.CredentialsGroup{ - DisplayName: utils.Ptr("name"), - Urn: utils.Ptr("urn"), + CredentialsGroup: objectstorage.CredentialsGroup{ + DisplayName: "name", + Urn: "urn", }, }, Model{ @@ -79,9 +80,9 @@ func TestMapFields(t *testing.T) { { "empty_strings", &objectstorage.CreateCredentialsGroupResponse{ - CredentialsGroup: &objectstorage.CredentialsGroup{ - DisplayName: utils.Ptr(""), - Urn: utils.Ptr(""), + CredentialsGroup: objectstorage.CredentialsGroup{ + DisplayName: "", + Urn: "", }, }, Model{ @@ -149,9 +150,10 @@ func TestEnableProject(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - client := &objectStorageClientMocked{ + client := newAPIMock(&mockSettings{ returnError: tt.enableFails, - } + }) + err := enableProject(context.Background(), &Model{}, "eu01", client) if !tt.isValid && err == nil { t.Fatalf("Should have failed") @@ -177,21 +179,21 @@ func TestReadCredentialsGroups(t *testing.T) { { "default_values", &objectstorage.ListCredentialsGroupsResponse{ - CredentialsGroups: &[]objectstorage.CredentialsGroup{ + CredentialsGroups: []objectstorage.CredentialsGroup{ { - CredentialsGroupId: utils.Ptr("cid"), + CredentialsGroupId: "cid", }, { - CredentialsGroupId: utils.Ptr("foo-id"), + CredentialsGroupId: "foo-id", }, }, }, Model{ Id: types.StringValue(id), - Name: types.StringNull(), + Name: types.StringValue(""), ProjectId: types.StringValue("pid"), CredentialsGroupId: types.StringValue("cid"), - URN: types.StringNull(), + URN: types.StringValue(""), }, true, false, @@ -200,16 +202,16 @@ func TestReadCredentialsGroups(t *testing.T) { { "simple_values", &objectstorage.ListCredentialsGroupsResponse{ - CredentialsGroups: &[]objectstorage.CredentialsGroup{ + CredentialsGroups: []objectstorage.CredentialsGroup{ { - CredentialsGroupId: utils.Ptr("cid"), - DisplayName: utils.Ptr("name"), - Urn: utils.Ptr("urn"), + CredentialsGroupId: "cid", + DisplayName: "name", + Urn: "urn", }, { - CredentialsGroupId: utils.Ptr("foo-cid"), - DisplayName: utils.Ptr("foo-name"), - Urn: utils.Ptr("foo-urn"), + CredentialsGroupId: "foo-cid", + DisplayName: "foo-name", + Urn: "foo-urn", }, }, }, @@ -227,7 +229,7 @@ func TestReadCredentialsGroups(t *testing.T) { { "empty_credentials_groups", &objectstorage.ListCredentialsGroupsResponse{ - CredentialsGroups: &[]objectstorage.CredentialsGroup{}, + CredentialsGroups: []objectstorage.CredentialsGroup{}, }, Model{}, false, @@ -255,11 +257,11 @@ func TestReadCredentialsGroups(t *testing.T) { { "non_matching_credentials_group", &objectstorage.ListCredentialsGroupsResponse{ - CredentialsGroups: &[]objectstorage.CredentialsGroup{ + CredentialsGroups: []objectstorage.CredentialsGroup{ { - CredentialsGroupId: utils.Ptr("foo-other"), - DisplayName: utils.Ptr("foo-name"), - Urn: utils.Ptr("foo-urn"), + CredentialsGroupId: "foo-other", + DisplayName: "foo-name", + Urn: "foo-urn", }, }, }, @@ -271,11 +273,11 @@ func TestReadCredentialsGroups(t *testing.T) { { "error_response", &objectstorage.ListCredentialsGroupsResponse{ - CredentialsGroups: &[]objectstorage.CredentialsGroup{ + CredentialsGroups: []objectstorage.CredentialsGroup{ { - CredentialsGroupId: utils.Ptr("other_id"), - DisplayName: utils.Ptr("name"), - Urn: utils.Ptr("urn"), + CredentialsGroupId: "other_id", + DisplayName: "name", + Urn: "urn", }, }, }, @@ -287,10 +289,11 @@ func TestReadCredentialsGroups(t *testing.T) { } for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { - client := &objectStorageClientMocked{ + client := newAPIMock(&mockSettings{ returnError: tt.getCredentialsGroupsFails, listCredentialsGroupsResp: tt.mockedResp, - } + }) + model := &Model{ ProjectId: tt.expectedModel.ProjectId, CredentialsGroupId: tt.expectedModel.CredentialsGroupId, diff --git a/stackit/internal/services/objectstorage/objectstorage_acc_test.go b/stackit/internal/services/objectstorage/objectstorage_acc_test.go index ff5e6dcd4..9f3c0820e 100644 --- a/stackit/internal/services/objectstorage/objectstorage_acc_test.go +++ b/stackit/internal/services/objectstorage/objectstorage_acc_test.go @@ -15,8 +15,8 @@ import ( stackitSdkConfig "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/wait" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" + "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api/wait" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" ) @@ -288,9 +288,7 @@ func testAccCheckObjectStorageDestroy(s *terraform.State) error { var client *objectstorage.APIClient var err error if testutil.ObjectStorageCustomEndpoint == "" { - client, err = objectstorage.NewAPIClient( - stackitSdkConfig.WithRegion("eu01"), - ) + client, err = objectstorage.NewAPIClient() } else { client, err = objectstorage.NewAPIClient( stackitSdkConfig.WithEndpoint(testutil.ObjectStorageCustomEndpoint), @@ -310,23 +308,20 @@ func testAccCheckObjectStorageDestroy(s *terraform.State) error { bucketsToDestroy = append(bucketsToDestroy, bucketName) } - bucketsResp, err := client.ListBuckets(ctx, testutil.ProjectId, testutil.Region).Execute() + bucketsResp, err := client.DefaultAPI.ListBuckets(ctx, testutil.ProjectId, testutil.Region).Execute() if err != nil { return fmt.Errorf("getting bucketsResp: %w", err) } - buckets := *bucketsResp.Buckets + buckets := bucketsResp.Buckets for _, bucket := range buckets { - if bucket.Name == nil { - continue - } - bucketName := *bucket.Name + bucketName := bucket.Name if utils.Contains(bucketsToDestroy, bucketName) { - _, err := client.DeleteBucketExecute(ctx, testutil.ProjectId, testutil.Region, bucketName) + _, err := client.DefaultAPI.DeleteBucket(ctx, testutil.ProjectId, testutil.Region, bucketName).Execute() if err != nil { return fmt.Errorf("destroying bucket %s during CheckDestroy: %w", bucketName, err) } - _, err = wait.DeleteBucketWaitHandler(ctx, client, testutil.ProjectId, testutil.Region, bucketName).WaitWithContext(ctx) + _, err = wait.DeleteBucketWaitHandler(ctx, client.DefaultAPI, testutil.ProjectId, testutil.Region, bucketName).WaitWithContext(ctx) if err != nil { return fmt.Errorf("destroying instance %s during CheckDestroy: waiting for deletion %w", bucketName, err) } @@ -343,19 +338,16 @@ func testAccCheckObjectStorageDestroy(s *terraform.State) error { credentialsGroupsToDestroy = append(credentialsGroupsToDestroy, credentialsGroupId) } - credentialsGroupsResp, err := client.ListCredentialsGroups(ctx, testutil.ProjectId, testutil.Region).Execute() + credentialsGroupsResp, err := client.DefaultAPI.ListCredentialsGroups(ctx, testutil.ProjectId, testutil.Region).Execute() if err != nil { return fmt.Errorf("getting bucketsResp: %w", err) } - groups := *credentialsGroupsResp.CredentialsGroups + groups := credentialsGroupsResp.CredentialsGroups for _, group := range groups { - if group.CredentialsGroupId == nil { - continue - } - groupId := *group.CredentialsGroupId + groupId := group.CredentialsGroupId if utils.Contains(credentialsGroupsToDestroy, groupId) { - _, err := client.DeleteCredentialsGroupExecute(ctx, testutil.ProjectId, testutil.Region, groupId) + _, err := client.DefaultAPI.DeleteCredentialsGroup(ctx, testutil.ProjectId, testutil.Region, groupId).Execute() if err != nil { return fmt.Errorf("destroying credentials group %s during CheckDestroy: %w", groupId, err) } diff --git a/stackit/internal/services/objectstorage/objectstorage_test.go b/stackit/internal/services/objectstorage/objectstorage_test.go index cdac8560c..c5997017d 100644 --- a/stackit/internal/services/objectstorage/objectstorage_test.go +++ b/stackit/internal/services/objectstorage/objectstorage_test.go @@ -8,8 +8,7 @@ import ( "github.com/google/uuid" "github.com/hashicorp/terraform-plugin-testing/helper/resource" - "github.com/stackitcloud/stackit-sdk-go/core/utils" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/testutil" ) @@ -42,15 +41,15 @@ resource "stackit_objectstorage_bucket" "instance" { testutil.MockResponse{ Description: "project enable", ToJsonBody: objectstorage.ProjectStatus{ - Project: utils.Ptr(projectId), - Scope: utils.Ptr(objectstorage.PROJECTSCOPE_PUBLIC), + Project: projectId, + Scope: objectstorage.PROJECTSCOPE_PUBLIC, }, }, testutil.MockResponse{ Description: "create bucket", ToJsonBody: objectstorage.Bucket{ - Name: utils.Ptr(name), - Region: utils.Ptr(region), + Name: name, + Region: region, }, }, testutil.MockResponse{ diff --git a/stackit/internal/services/objectstorage/utils/util.go b/stackit/internal/services/objectstorage/utils/util.go index 56a013a20..1f43a2b91 100644 --- a/stackit/internal/services/objectstorage/utils/util.go +++ b/stackit/internal/services/objectstorage/utils/util.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" "github.com/hashicorp/terraform-plugin-framework/diag" "github.com/stackitcloud/stackit-sdk-go/core/config" diff --git a/stackit/internal/services/objectstorage/utils/util_test.go b/stackit/internal/services/objectstorage/utils/util_test.go index d31dc8a6f..db64b89ae 100644 --- a/stackit/internal/services/objectstorage/utils/util_test.go +++ b/stackit/internal/services/objectstorage/utils/util_test.go @@ -9,7 +9,7 @@ import ( "github.com/hashicorp/terraform-plugin-framework/diag" sdkClients "github.com/stackitcloud/stackit-sdk-go/core/clients" "github.com/stackitcloud/stackit-sdk-go/core/config" - "github.com/stackitcloud/stackit-sdk-go/services/objectstorage" + objectstorage "github.com/stackitcloud/stackit-sdk-go/services/objectstorage/v2api" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/core" "github.com/stackitcloud/terraform-provider-stackit/stackit/internal/utils" )