From e947f41499ea4ece33427b58880f0fcf1cf6f324 Mon Sep 17 00:00:00 2001 From: Jonas Schlecht Date: Fri, 19 Jun 2026 10:38:42 +0200 Subject: [PATCH 1/3] feat(rabbitmq): Add wait handlers for v2 package Relates to STACKITSDK-465 --- CHANGELOG.md | 6 + examples/rabbitmq/rabbitmq.go | 9 +- services/rabbitmq/CHANGELOG.md | 4 + services/rabbitmq/VERSION | 2 +- services/rabbitmq/v2api/wait/wait.go | 134 +++++++ services/rabbitmq/v2api/wait/wait_test.go | 410 ++++++++++++++++++++++ 6 files changed, 560 insertions(+), 5 deletions(-) create mode 100644 services/rabbitmq/v2api/wait/wait.go create mode 100644 services/rabbitmq/v2api/wait/wait_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d06c51c4..4216d9911 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +## Release (upcoming) +- `rabbitmq`: + - [v1.1.0](/services/rabbitmq/CHANGELOG.md#v110) + - `v2api`: + - Feature: Added wait handlers + ## Release (2026-06-18) - `core`: - [v0.25.0](core/CHANGELOG.md#v0250) diff --git a/examples/rabbitmq/rabbitmq.go b/examples/rabbitmq/rabbitmq.go index b38231750..8072afbb6 100644 --- a/examples/rabbitmq/rabbitmq.go +++ b/examples/rabbitmq/rabbitmq.go @@ -6,11 +6,12 @@ import ( "os" "github.com/stackitcloud/stackit-sdk-go/core/config" - rabbitmq "github.com/stackitcloud/stackit-sdk-go/services/rabbitmq/v1api" + rabbitmq "github.com/stackitcloud/stackit-sdk-go/services/rabbitmq/v2api" ) func main() { projectId := "PROJECT_ID" // the uuid of your STACKIT project + region := "eu01" planId := "PLAN_ID" // Create a new API client, that uses default authentication and configuration @@ -23,7 +24,7 @@ func main() { } // Get the rabbitmq instances for your project - getInstancesResp, err := rabbitmqClient.DefaultAPI.ListInstances(context.Background(), projectId).Execute() + getInstancesResp, err := rabbitmqClient.DefaultAPI.ListInstances(context.Background(), projectId, region).Execute() if err != nil { fmt.Fprintf(os.Stderr, "Error when calling `GetInstances`: %v\n", err) } else { @@ -31,7 +32,7 @@ func main() { } // Get the rabbitmq offerings for your project - getOfferingsResp, err := rabbitmqClient.DefaultAPI.ListOfferings(context.Background(), projectId).Execute() + getOfferingsResp, err := rabbitmqClient.DefaultAPI.ListOfferings(context.Background(), projectId, region).Execute() if err != nil { fmt.Fprintf(os.Stderr, "Error when calling `GetOfferings`: %v\n", err) } else { @@ -44,7 +45,7 @@ func main() { Parameters: &rabbitmq.InstanceParameters{}, PlanId: planId, } - createInstanceResp, err := rabbitmqClient.DefaultAPI.CreateInstance(context.Background(), projectId).CreateInstancePayload(createInstancePayload).Execute() + createInstanceResp, err := rabbitmqClient.DefaultAPI.CreateInstance(context.Background(), projectId, region).CreateInstancePayload(createInstancePayload).Execute() if err != nil { fmt.Fprintf(os.Stderr, "Error when calling `CreateInstance`: %v\n", err) } else { diff --git a/services/rabbitmq/CHANGELOG.md b/services/rabbitmq/CHANGELOG.md index 540ba9574..115f2e36f 100644 --- a/services/rabbitmq/CHANGELOG.md +++ b/services/rabbitmq/CHANGELOG.md @@ -1,3 +1,7 @@ +## v1.1.0 +- `v2api`: + - Feature: Added wait handlers + ## v1.0.0 - **Breaking Change:** The region is no longer specified within the client configuration. Instead, the region must be passed as a parameter to any region-specific request. - `v2api`: diff --git a/services/rabbitmq/VERSION b/services/rabbitmq/VERSION index 60453e690..795460fce 100644 --- a/services/rabbitmq/VERSION +++ b/services/rabbitmq/VERSION @@ -1 +1 @@ -v1.0.0 \ No newline at end of file +v1.1.0 diff --git a/services/rabbitmq/v2api/wait/wait.go b/services/rabbitmq/v2api/wait/wait.go new file mode 100644 index 000000000..3b15af5e7 --- /dev/null +++ b/services/rabbitmq/v2api/wait/wait.go @@ -0,0 +1,134 @@ +package wait + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" + "time" + + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/wait" + rabbitmq "github.com/stackitcloud/stackit-sdk-go/services/rabbitmq/v2api" +) + +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, client rabbitmq.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[rabbitmq.Instance] { + waitConfig := wait.WaiterHelper[rabbitmq.Instance, rabbitmq.InstanceStatus]{ + FetchInstance: client.GetInstance(ctx, projectId, region, instanceId).Execute, + GetState: func(response *rabbitmq.Instance) (rabbitmq.InstanceStatus, error) { + if response == nil { + return "", errors.New("empty response") + } + if response.Status == nil { + return "", errors.New("status is missing in response") + } + return *response.Status, nil + }, + ActiveState: []rabbitmq.InstanceStatus{rabbitmq.INSTANCESTATUS_ACTIVE}, + ErrorState: []rabbitmq.InstanceStatus{rabbitmq.INSTANCESTATUS_FAILED}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(45 * time.Minute) + return handler +} + +// PartialUpdateInstanceWaitHandler will wait for instance update +func PartialUpdateInstanceWaitHandler(ctx context.Context, client rabbitmq.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[rabbitmq.Instance] { + waitConfig := wait.WaiterHelper[rabbitmq.Instance, rabbitmq.InstanceStatus]{ + FetchInstance: client.GetInstance(ctx, projectId, region, instanceId).Execute, + GetState: func(response *rabbitmq.Instance) (rabbitmq.InstanceStatus, error) { + if response == nil { + return "", errors.New("empty response") + } + if response.Status == nil { + return "", errors.New("status is missing in response") + } + return *response.Status, nil + }, + ActiveState: []rabbitmq.InstanceStatus{rabbitmq.INSTANCESTATUS_ACTIVE}, + ErrorState: []rabbitmq.InstanceStatus{rabbitmq.INSTANCESTATUS_FAILED}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(45 * time.Minute) + return handler +} + +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] { + handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { + s, err := a.GetInstance(ctx, projectId, region, instanceId).Execute() + if err == nil { + if s.Status == nil { + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The status is missing", instanceId) + } + if *s.Status != rabbitmq.INSTANCESTATUS_DELETING { + return false, nil, nil + } + if *s.Status == rabbitmq.INSTANCESTATUS_ACTIVE { + if strings.Contains(s.LastOperation.Description, "DeleteFailed") || strings.Contains(s.LastOperation.Description, "failed") { + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", s.LastOperation.Description) + } + return true, nil, nil + } + return false, nil, 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 { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusGone { + return false, nil, err + } + return true, nil, nil + }) + handler.SetTimeout(15 * time.Minute) + return handler +} + +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, projectId, region, instanceId, credentialsId string) *wait.AsyncActionHandler[rabbitmq.CredentialsResponse] { + handler := wait.New(func() (waitFinished bool, response *rabbitmq.CredentialsResponse, err error) { + s, err := a.GetCredentials(ctx, projectId, region, instanceId, credentialsId).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 { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + // If the request returns 404, the credentials have not been created yet + if oapiErr.StatusCode == http.StatusNotFound { + return false, nil, nil + } + return false, nil, err + } + if s.Id == credentialsId { + return true, s, nil + } + return false, nil, nil + }) + handler.SetTimeout(1 * time.Minute) + return handler +} + +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, projectId, region, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + handler := wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentials(ctx, projectId, region, instanceId, credentialsId).Execute() + if err == nil { + return false, nil, 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 { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err + } + return true, nil, nil + }) + handler.SetTimeout(1 * time.Minute) + return handler +} diff --git a/services/rabbitmq/v2api/wait/wait_test.go b/services/rabbitmq/v2api/wait/wait_test.go new file mode 100644 index 000000000..1bb9b9be6 --- /dev/null +++ b/services/rabbitmq/v2api/wait/wait_test.go @@ -0,0 +1,410 @@ +package wait + +import ( + "context" + "testing" + "testing/synctest" + "time" + + "github.com/google/go-cmp/cmp" + + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + rabbitmq "github.com/stackitcloud/stackit-sdk-go/services/rabbitmq/v2api" +) + +type mockSettings struct { + instanceGetFails bool + instanceDeletionSucceedsWithErrors bool + instanceResourceId string + instanceResourceOperation rabbitmq.InstanceLastOperationType + instanceResourceState *rabbitmq.InstanceStatus + instanceResourceDescription string + + credentialGetFails bool + credentialResourceId string + credentialOperationSucceeds bool + credentialDeletionSucceeds bool +} + +const testRegion = "eu01" + +func newAPIMock(settings *mockSettings) rabbitmq.DefaultAPI { + return &rabbitmq.DefaultAPIServiceMock{ + GetInstanceExecuteMock: utils.Ptr(func(_ rabbitmq.ApiGetInstanceRequest) (*rabbitmq.Instance, error) { + if settings.instanceGetFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: 500, + } + } + if settings.instanceResourceOperation == rabbitmq.INSTANCELASTOPERATIONTYPE_DELETE && settings.instanceResourceState != nil && *settings.instanceResourceState == rabbitmq.INSTANCESTATUS_ACTIVE { + if settings.instanceDeletionSucceedsWithErrors { + return &rabbitmq.Instance{ + InstanceId: &settings.instanceResourceId, + Status: settings.instanceResourceState, + LastOperation: rabbitmq.InstanceLastOperation{ + Description: settings.instanceResourceDescription, + Type: settings.instanceResourceOperation, + }, + }, nil + } + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: 410, + } + } + + return &rabbitmq.Instance{ + InstanceId: &settings.instanceResourceId, + Status: settings.instanceResourceState, + }, nil + }), + GetCredentialsExecuteMock: utils.Ptr(func(_ rabbitmq.ApiGetCredentialsRequest) (*rabbitmq.CredentialsResponse, error) { + if settings.credentialGetFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: 500, + } + } + + if !settings.credentialOperationSucceeds || settings.credentialDeletionSucceeds { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: 404, + } + } + + return &rabbitmq.CredentialsResponse{ + Id: settings.credentialResourceId, + }, nil + }), + } +} + +func TestCreateInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + resourceState *rabbitmq.InstanceStatus + wantErr bool + wantResp bool + }{ + { + desc: "create_succeeded", + getFails: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_ACTIVE), + wantErr: false, + wantResp: true, + }, + { + desc: "create_failed", + getFails: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_FAILED), + wantErr: true, + wantResp: true, + }, + { + desc: "wrong state in response", + getFails: false, + resourceState: utils.Ptr(rabbitmq.InstanceStatus("wrong state")), + wantErr: true, + wantResp: false, + }, + { + desc: "get_fails", + getFails: true, + wantErr: true, + wantResp: false, + }, + { + desc: "timeout", + getFails: false, + resourceState: utils.Ptr(rabbitmq.InstanceStatus("ANOTHER STATE")), + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + instanceId := "foo-bar" + + apiClient := newAPIMock(&mockSettings{ + instanceGetFails: tt.getFails, + instanceResourceId: instanceId, + instanceResourceState: tt.resourceState, + }) + + var wantRes *rabbitmq.Instance + if tt.wantResp { + wantRes = &rabbitmq.Instance{ + InstanceId: &instanceId, + Status: tt.resourceState, + } + } + + handler := CreateInstanceWaitHandler(context.Background(), apiClient, "pid", testRegion, instanceId) + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + diff := cmp.Diff(gotRes, wantRes) + if diff != "" { + t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff) + } + }) + }) + } +} + +func TestUpdateInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + resourceState *rabbitmq.InstanceStatus + wantErr bool + wantResp bool + }{ + { + desc: "update_succeeded", + getFails: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_ACTIVE), + wantErr: false, + wantResp: true, + }, + { + desc: "update_failed", + getFails: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_FAILED), + wantErr: true, + wantResp: true, + }, + { + desc: "wrong state in response", + getFails: false, + resourceState: utils.Ptr(rabbitmq.InstanceStatus("wrong state")), + wantErr: true, + wantResp: false, + }, + { + desc: "get_fails", + getFails: true, + wantErr: true, + wantResp: false, + }, + { + desc: "timeout", + getFails: false, + resourceState: utils.Ptr(rabbitmq.InstanceStatus("ANOTHER STATE")), + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + instanceId := "foo-bar" + + apiClient := newAPIMock(&mockSettings{ + instanceGetFails: tt.getFails, + instanceResourceId: instanceId, + instanceResourceState: tt.resourceState, + }) + + var wantRes *rabbitmq.Instance + if tt.wantResp { + wantRes = &rabbitmq.Instance{ + InstanceId: &instanceId, + Status: tt.resourceState, + } + } + + handler := PartialUpdateInstanceWaitHandler(context.Background(), apiClient, "", testRegion, instanceId) + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestDeleteInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + deleteSucceeedsWithErrors bool + resourceState *rabbitmq.InstanceStatus + resourceDescription string + wantErr bool + }{ + { + desc: "delete_succeeded", + getFails: false, + deleteSucceeedsWithErrors: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_ACTIVE), + wantErr: false, + }, + { + desc: "delete_failed", + getFails: false, + deleteSucceeedsWithErrors: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_FAILED), + wantErr: true, + }, + { + desc: "delete_succeeds_with_errors", + getFails: false, + resourceState: utils.Ptr(rabbitmq.INSTANCESTATUS_ACTIVE), + deleteSucceeedsWithErrors: true, + resourceDescription: "Deleting resource: cf failed with error: DeleteFailed", + wantErr: true, + }, + { + desc: "get_fails", + deleteSucceeedsWithErrors: false, + getFails: true, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + instanceId := "foo-bar" + + apiClient := newAPIMock(&mockSettings{ + instanceGetFails: tt.getFails, + instanceDeletionSucceedsWithErrors: tt.deleteSucceeedsWithErrors, + instanceResourceId: instanceId, + instanceResourceOperation: rabbitmq.INSTANCELASTOPERATIONTYPE_DELETE, + instanceResourceDescription: tt.resourceDescription, + instanceResourceState: tt.resourceState, + }) + + handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", testRegion, instanceId) + + _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + }) + }) + } +} + +func TestCreateCredentialsWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + operationSucceeds bool + wantErr bool + wantResp bool + }{ + { + desc: "create_succeeded", + getFails: false, + operationSucceeds: true, + wantErr: false, + wantResp: true, + }, + { + desc: "create_failed", + getFails: false, + operationSucceeds: false, + wantErr: true, + wantResp: false, + }, + { + desc: "get_fails", + getFails: true, + wantErr: true, + wantResp: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + credentialsId := "foo-bar" + + apiClient := newAPIMock(&mockSettings{ + credentialGetFails: tt.getFails, + credentialResourceId: credentialsId, + credentialOperationSucceeds: tt.operationSucceeds, + }) + + var wantRes *rabbitmq.CredentialsResponse + if tt.wantResp { + wantRes = &rabbitmq.CredentialsResponse{ + Id: credentialsId, + } + } + + handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", testRegion, "", credentialsId) + + gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(gotRes, wantRes) { + t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) + } + }) + }) + } +} + +func TestDeleteCredentialsWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + deletionSucceeds bool + wantErr bool + }{ + { + desc: "delete_succeeded", + getFails: false, + deletionSucceeds: true, + wantErr: false, + }, + { + desc: "delete_failed", + getFails: false, + deletionSucceeds: false, + wantErr: true, + }, + { + desc: "get_fails", + getFails: true, + deletionSucceeds: false, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + credentialsId := "foo-bar" + + apiClient := newAPIMock(&mockSettings{ + credentialGetFails: tt.getFails, + credentialResourceId: credentialsId, + credentialOperationSucceeds: true, + credentialDeletionSucceeds: tt.deletionSucceeds, + }) + + handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", testRegion, "", credentialsId) + + _, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + }) + }) + } +} From 5b4cd88a7a363a8e707a089ef5cf06998d410681 Mon Sep 17 00:00:00 2001 From: Jonas Schlecht Date: Fri, 19 Jun 2026 13:22:42 +0200 Subject: [PATCH 2/3] refactor(rabbitmq): apply review feedback --- CHANGELOG.md | 4 ++-- services/rabbitmq/CHANGELOG.md | 2 +- services/rabbitmq/v2api/wait/wait.go | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4216d9911..49aecd618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ ## Release (upcoming) - `rabbitmq`: - [v1.1.0](/services/rabbitmq/CHANGELOG.md#v110) - - `v2api`: - - Feature: Added wait handlers + - `v2api`: + - **Feature**: Added wait handlers ## Release (2026-06-18) - `core`: diff --git a/services/rabbitmq/CHANGELOG.md b/services/rabbitmq/CHANGELOG.md index 115f2e36f..f53c7461c 100644 --- a/services/rabbitmq/CHANGELOG.md +++ b/services/rabbitmq/CHANGELOG.md @@ -1,6 +1,6 @@ ## v1.1.0 - `v2api`: - - Feature: Added wait handlers + - **Feature**: Added wait handlers ## v1.0.0 - **Breaking Change:** The region is no longer specified within the client configuration. Instead, the region must be passed as a parameter to any region-specific request. diff --git a/services/rabbitmq/v2api/wait/wait.go b/services/rabbitmq/v2api/wait/wait.go index 3b15af5e7..dd9047ef5 100644 --- a/services/rabbitmq/v2api/wait/wait.go +++ b/services/rabbitmq/v2api/wait/wait.go @@ -65,9 +65,6 @@ func DeleteInstanceWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, proje if s.Status == nil { return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The status is missing", instanceId) } - if *s.Status != rabbitmq.INSTANCESTATUS_DELETING { - return false, nil, nil - } if *s.Status == rabbitmq.INSTANCESTATUS_ACTIVE { if strings.Contains(s.LastOperation.Description, "DeleteFailed") || strings.Contains(s.LastOperation.Description, "failed") { return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", s.LastOperation.Description) @@ -76,7 +73,8 @@ func DeleteInstanceWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, proje } return false, nil, 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 + var oapiErr *oapierror.GenericOpenAPIError + ok := errors.As(err, &oapiErr) if !ok { return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } @@ -94,7 +92,8 @@ func CreateCredentialsWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, pr handler := wait.New(func() (waitFinished bool, response *rabbitmq.CredentialsResponse, err error) { s, err := a.GetCredentials(ctx, projectId, region, instanceId, credentialsId).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 + var oapiErr *oapierror.GenericOpenAPIError + ok := errors.As(err, &oapiErr) if !ok { return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } @@ -120,7 +119,8 @@ func DeleteCredentialsWaitHandler(ctx context.Context, a rabbitmq.DefaultAPI, pr if err == nil { return false, nil, 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 + var oapiErr *oapierror.GenericOpenAPIError + ok := errors.As(err, &oapiErr) if !ok { return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } From 01646b4c167a30cadf6e2303ff2fb1b4f04c3833 Mon Sep 17 00:00:00 2001 From: Jonas Schlecht Date: Fri, 19 Jun 2026 13:49:35 +0200 Subject: [PATCH 3/3] feat(rabbitmq): expand rabbitmq example with wait and cleanup instance feat(rabbitmq): expand rabbitmq example with wait and cleanup instance --- examples/rabbitmq/rabbitmq.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/examples/rabbitmq/rabbitmq.go b/examples/rabbitmq/rabbitmq.go index 8072afbb6..0689e78ca 100644 --- a/examples/rabbitmq/rabbitmq.go +++ b/examples/rabbitmq/rabbitmq.go @@ -7,6 +7,7 @@ import ( "github.com/stackitcloud/stackit-sdk-go/core/config" rabbitmq "github.com/stackitcloud/stackit-sdk-go/services/rabbitmq/v2api" + wait "github.com/stackitcloud/stackit-sdk-go/services/rabbitmq/v2api/wait" ) func main() { @@ -51,4 +52,27 @@ func main() { } else { fmt.Printf("Created instance with instance id \"%s\".\n", createInstanceResp.InstanceId) } + + // Wait for creation of rabbitmq instance + instance, err := wait.CreateInstanceWaitHandler(context.Background(), rabbitmqClient.DefaultAPI, projectId, region, createInstanceResp.InstanceId).WaitWithContext(context.Background()) + if err != nil { + fmt.Fprintf(os.Stderr, "Error when waiting for creation: %v\n", err) + os.Exit(1) + } + fmt.Printf("Rabbitmq instance %v has been successfully created.", instance.InstanceId) + + // Delete a rabbitmq instance + err = rabbitmqClient.DefaultAPI.DeleteInstance(context.Background(), projectId, region, *instance.InstanceId).Execute() + if err != nil { + fmt.Fprintf(os.Stderr, "Error when calling 'DeleteInstance': %v\n", err) + os.Exit(1) + } + + // Wait for deletaion of rabbitmq instance + _, err = wait.DeleteInstanceWaitHandler(context.Background(), rabbitmqClient.DefaultAPI, projectId, region, *instance.InstanceId).WaitWithContext(context.Background()) + if err != nil { + fmt.Fprintf(os.Stderr, "Error when waiting for deletion: %v\n", err) + os.Exit(1) + } + fmt.Printf("Rabbitmq instance %v has been successfully deleted.", instance.InstanceId) }