Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## Release (2025-xx-xx)
- `scf`:
- [v0.2.1](services/scf/CHANGELOG.md#v021)
- **Feature:** Add waiter for deletion of organization

## Release (2025-08-13)
- `scf`:
- [v0.2.0](services/scf/CHANGELOG.md#v020)
Expand Down
3 changes: 3 additions & 0 deletions services/scf/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## v0.2.1
- **Feature:** Add waiter for deletion of organization

## v0.2.0
- **Feature:** Add field `OrgId` in model `OrgManager`
- **Feature:** Add new model `OrganizationCreateBffResponse` and `SpaceCreatedBffResponse`
Expand Down
2 changes: 1 addition & 1 deletion services/scf/VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v0.2.0
v0.2.1
44 changes: 44 additions & 0 deletions services/scf/wait/wait.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package wait

import (
"context"
"errors"
"fmt"
"net/http"
"time"

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/core/wait"
"github.com/stackitcloud/stackit-sdk-go/services/scf"
)

const statusDeletingFailed = "deleting_failed"

// Interfaces needed for tests
type APIClientInterface interface {
GetOrganizationExecute(ctx context.Context, projectId, region, orgId string) (*scf.Organization, error)
}

// DeleteOrganizationWaitHandler will wait for Organization deletion
func DeleteOrganizationWaitHandler(ctx context.Context, a APIClientInterface, projectId, region, orgId string) *wait.AsyncActionHandler[scf.Organization] {
handler := wait.New(func() (waitFinished bool, response *scf.Organization, err error) {
s, err := a.GetOrganizationExecute(ctx, projectId, region, orgId)
if err != nil {
var oapiErr *oapierror.GenericOpenAPIError
ok := errors.As(err, &oapiErr)
if ok && oapiErr.StatusCode == http.StatusNotFound {
return true, s, nil
}
return false, s, err
}
if s == nil {
return false, nil, errors.New("organization is nil")
}
if *s.Status == statusDeletingFailed {
return true, nil, fmt.Errorf("delete failed for Organization with id %s", orgId)
}
return false, s, nil
})
handler.SetTimeout(20 * time.Minute)
return handler
}
99 changes: 99 additions & 0 deletions services/scf/wait/wait_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package wait

import (
"context"
"testing"
"time"

"github.com/google/uuid"

"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
"github.com/stackitcloud/stackit-sdk-go/services/scf"
)

var PROJECT_ID = uuid.New().String()
var INSTANCE_ID = uuid.New().String()

const REGION = "eu01"

type apiClientMocked struct {
getFails bool
errorCode int
returnInstance bool
projectId string
instanceId string
getSCFResponse *scf.Organization
}

func (a *apiClientMocked) GetOrganizationExecute(_ context.Context, _, _, _ string) (*scf.Organization, error) {
if a.getFails {
return nil, &oapierror.GenericOpenAPIError{
StatusCode: a.errorCode,
}
}
if !a.returnInstance {
return nil, nil
}
return a.getSCFResponse, nil
}

func TestDeleteOrganizationWaitHandler(t *testing.T) {
statusDeletingFailed := "deleting_failed"
tests := []struct {
desc string
wantErr bool
wantReturnedInstance bool
getFails bool
errorCode int
returnInstance bool
getOrgResponse *scf.Organization
}{
{
desc: "Instance deletion failed with error",
wantErr: true,
getFails: true,
},
{
desc: "Instance is not found",
wantErr: false,
getFails: true,
errorCode: 404,
},
{
desc: "Instance is in error state",
wantErr: true,
returnInstance: true,
getOrgResponse: &scf.Organization{
Status: &statusDeletingFailed,
},
},
{
desc: "Instance is nil",
wantErr: true,
returnInstance: true,
getOrgResponse: nil,
},
}
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
apiClient := &apiClientMocked{
projectId: PROJECT_ID,
instanceId: INSTANCE_ID,
getFails: tt.getFails,
errorCode: tt.errorCode,
returnInstance: tt.returnInstance,
getSCFResponse: tt.getOrgResponse,
}

handler := DeleteOrganizationWaitHandler(context.Background(), apiClient, apiClient.projectId, REGION, apiClient.instanceId)
response, 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 (response != nil) != tt.wantReturnedInstance {
t.Fatalf("handler gotRes = %v, want nil", response)
}
})
}
}
Loading