Skip to content

Commit 43b7c2f

Browse files
Check for service enablement before bucket creation (#398)
* Check for service enablement before bucket creation * Fix comment * Fix linter * Improve variable naming --------- Co-authored-by: [email protected] <[email protected]>
1 parent 02ea25d commit 43b7c2f

File tree

7 files changed

+135
-3
lines changed

7 files changed

+135
-3
lines changed

internal/cmd/object-storage/bucket/create/create.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1313
"github.com/stackitcloud/stackit-cli/internal/pkg/print"
1414
"github.com/stackitcloud/stackit-cli/internal/pkg/services/object-storage/client"
15+
"github.com/stackitcloud/stackit-cli/internal/pkg/services/object-storage/utils"
1516
"github.com/stackitcloud/stackit-cli/internal/pkg/spinner"
1617

1718
"github.com/spf13/cobra"
@@ -60,6 +61,17 @@ func NewCmd(p *print.Printer) *cobra.Command {
6061
}
6162
}
6263

64+
// Check if the project is enabled before trying to create
65+
enabled, err := utils.ProjectEnabled(ctx, apiClient, model.ProjectId)
66+
if err != nil {
67+
return fmt.Errorf("check if Object Storage is enabled: %w", err)
68+
}
69+
if !enabled {
70+
return &errors.ServiceDisabledError{
71+
Service: "object-storage",
72+
}
73+
}
74+
6375
// Call API
6476
req := buildRequest(ctx, model, apiClient)
6577
resp, err := req.Execute()

internal/cmd/ske/cluster/create/create.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,13 +87,15 @@ func NewCmd(p *print.Printer) *cobra.Command {
8787
}
8888
}
8989

90-
// Check if SKE is enabled for this project
90+
// Check if the project is enabled before trying to create
9191
enabled, err := skeUtils.ProjectEnabled(ctx, apiClient, model.ProjectId)
9292
if err != nil {
9393
return err
9494
}
9595
if !enabled {
96-
return fmt.Errorf("SKE isn't enabled for this project, please run 'stackit ske enable'")
96+
return &errors.ServiceDisabledError{
97+
Service: "ske",
98+
}
9799
}
98100

99101
// Check if cluster exists

internal/pkg/errors/errors.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,11 @@ The profile name can only contain lowercase letters, numbers, and "-" and cannot
133133

134134
USAGE_TIP = `For usage help, run:
135135
$ %s --help`
136+
137+
SERVICE_DISABLED = `This service isn't enabled for the current project.
138+
139+
To enable it, run:
140+
$ stackit %s enable`
136141
)
137142

138143
type ProjectIdError struct{}
@@ -364,3 +369,11 @@ type InvalidProfileNameError struct {
364369
func (e *InvalidProfileNameError) Error() string {
365370
return fmt.Sprintf(INVALID_PROFILE_NAME, e.Profile)
366371
}
372+
373+
type ServiceDisabledError struct {
374+
Service string
375+
}
376+
377+
func (e *ServiceDisabledError) Error() string {
378+
return fmt.Sprintf(SERVICE_DISABLED, e.Service)
379+
}

internal/pkg/services/object-storage/utils/utils.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,33 @@ package utils
33
import (
44
"context"
55
"fmt"
6+
"net/http"
67

8+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
79
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
810
)
911

1012
type ObjectStorageClient interface {
13+
GetServiceStatusExecute(ctx context.Context, projectId string) (*objectstorage.ProjectStatus, error)
1114
ListCredentialsGroupsExecute(ctx context.Context, projectId string) (*objectstorage.ListCredentialsGroupsResponse, error)
1215
ListAccessKeys(ctx context.Context, projectId string) objectstorage.ApiListAccessKeysRequest
1316
}
1417

18+
func ProjectEnabled(ctx context.Context, apiClient ObjectStorageClient, projectId string) (bool, error) {
19+
_, err := apiClient.GetServiceStatusExecute(ctx, projectId)
20+
if err != nil {
21+
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
22+
if !ok {
23+
return false, err
24+
}
25+
if oapiErr.StatusCode == http.StatusNotFound {
26+
return false, nil
27+
}
28+
return false, err
29+
}
30+
return true, nil
31+
}
32+
1533
func GetCredentialsGroupName(ctx context.Context, apiClient ObjectStorageClient, projectId, credentialsGroupId string) (string, error) {
1634
resp, err := apiClient.ListCredentialsGroupsExecute(ctx, projectId)
1735
if err != nil {

internal/pkg/services/object-storage/utils/utils_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"github.com/google/uuid"
1313
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1414
"github.com/stackitcloud/stackit-sdk-go/core/config"
15+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
1516
"github.com/stackitcloud/stackit-sdk-go/services/objectstorage"
1617
)
1718

@@ -27,11 +28,23 @@ const (
2728
)
2829

2930
type objectStorageClientMocked struct {
31+
serviceDisabled bool
32+
getServiceStatusFails bool
3033
listCredentialsGroupsFails bool
3134
listCredentialsGroupsResp *objectstorage.ListCredentialsGroupsResponse
3235
listAccessKeysReq objectstorage.ApiListAccessKeysRequest
3336
}
3437

38+
func (m *objectStorageClientMocked) GetServiceStatusExecute(_ context.Context, _ string) (*objectstorage.ProjectStatus, error) {
39+
if m.getServiceStatusFails {
40+
return nil, fmt.Errorf("could not get service status")
41+
}
42+
if m.serviceDisabled {
43+
return nil, &oapierror.GenericOpenAPIError{StatusCode: 404}
44+
}
45+
return &objectstorage.ProjectStatus{}, nil
46+
}
47+
3548
func (m *objectStorageClientMocked) ListCredentialsGroupsExecute(_ context.Context, _ string) (*objectstorage.ListCredentialsGroupsResponse, error) {
3649
if m.listCredentialsGroupsFails {
3750
return nil, fmt.Errorf("could not list credentials groups")
@@ -43,6 +56,58 @@ func (m *objectStorageClientMocked) ListAccessKeys(_ context.Context, _ string)
4356
return m.listAccessKeysReq
4457
}
4558

59+
func TestProjectEnabled(t *testing.T) {
60+
tests := []struct {
61+
description string
62+
serviceDisabled bool
63+
getProjectFails bool
64+
isValid bool
65+
expectedOutput bool
66+
}{
67+
{
68+
description: "project enabled",
69+
isValid: true,
70+
expectedOutput: true,
71+
},
72+
{
73+
description: "project disabled (404)",
74+
serviceDisabled: true,
75+
isValid: true,
76+
expectedOutput: false,
77+
},
78+
{
79+
description: "get project fails",
80+
getProjectFails: true,
81+
isValid: false,
82+
},
83+
}
84+
85+
for _, tt := range tests {
86+
t.Run(tt.description, func(t *testing.T) {
87+
client := &objectStorageClientMocked{
88+
serviceDisabled: tt.serviceDisabled,
89+
getServiceStatusFails: tt.getProjectFails,
90+
}
91+
92+
output, err := ProjectEnabled(context.Background(), client, testProjectId)
93+
94+
if tt.isValid && err != nil {
95+
fmt.Printf("failed on valid input: %v", err)
96+
t.Errorf("failed on valid input")
97+
}
98+
if !tt.isValid && err == nil {
99+
t.Errorf("did not fail on invalid input")
100+
}
101+
if !tt.isValid {
102+
return
103+
}
104+
if output != tt.expectedOutput {
105+
t.Errorf("expected output to be %t, got %t", tt.expectedOutput, output)
106+
}
107+
})
108+
}
109+
}
110+
46111
func TestGetCredentialsGroupName(t *testing.T) {
47112
tests := []struct {
48113
description string

internal/pkg/services/ske/utils/utils.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ package utils
33
import (
44
"context"
55
"fmt"
6+
"net/http"
67
"os"
78
"path/filepath"
89
"strconv"
910

1011
"github.com/stackitcloud/stackit-cli/internal/pkg/utils"
1112

13+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
1214
"github.com/stackitcloud/stackit-sdk-go/services/ske"
1315
"golang.org/x/mod/semver"
1416
)
@@ -37,7 +39,14 @@ type SKEClient interface {
3739
func ProjectEnabled(ctx context.Context, apiClient SKEClient, projectId string) (bool, error) {
3840
project, err := apiClient.GetServiceStatusExecute(ctx, projectId)
3941
if err != nil {
40-
return false, fmt.Errorf("get SKE status: %w", err)
42+
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
43+
if !ok {
44+
return false, err
45+
}
46+
if oapiErr.StatusCode == http.StatusNotFound {
47+
return false, nil
48+
}
49+
return false, err
4150
}
4251
return *project.State == ske.PROJECTSTATE_CREATED, nil
4352
}

internal/pkg/services/ske/utils/utils_test.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111

1212
"github.com/google/go-cmp/cmp"
1313
"github.com/google/uuid"
14+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
1415
"github.com/stackitcloud/stackit-sdk-go/services/ske"
1516
)
1617

@@ -23,6 +24,7 @@ const (
2324
)
2425

2526
type skeClientMocked struct {
27+
serviceDisabled bool
2628
getServiceStatusFails bool
2729
getServiceStatusResp *ske.ProjectResponse
2830
listClustersFails bool
@@ -35,6 +37,9 @@ func (m *skeClientMocked) GetServiceStatusExecute(_ context.Context, _ string) (
3537
if m.getServiceStatusFails {
3638
return nil, fmt.Errorf("could not get service status")
3739
}
40+
if m.serviceDisabled {
41+
return nil, &oapierror.GenericOpenAPIError{StatusCode: 404}
42+
}
3843
return m.getServiceStatusResp, nil
3944
}
4045

@@ -55,6 +60,7 @@ func (m *skeClientMocked) ListProviderOptionsExecute(_ context.Context) (*ske.Pr
5560
func TestProjectEnabled(t *testing.T) {
5661
tests := []struct {
5762
description string
63+
serviceDisabled bool
5864
getProjectFails bool
5965
getProjectResp *ske.ProjectResponse
6066
isValid bool
@@ -66,6 +72,12 @@ func TestProjectEnabled(t *testing.T) {
6672
isValid: true,
6773
expectedOutput: true,
6874
},
75+
{
76+
description: "project disabled (404)",
77+
serviceDisabled: true,
78+
isValid: true,
79+
expectedOutput: false,
80+
},
6981
{
7082
description: "project disabled 1",
7183
getProjectResp: &ske.ProjectResponse{State: ske.PROJECTSTATE_CREATING.Ptr()},
@@ -88,6 +100,7 @@ func TestProjectEnabled(t *testing.T) {
88100
for _, tt := range tests {
89101
t.Run(tt.description, func(t *testing.T) {
90102
client := &skeClientMocked{
103+
serviceDisabled: tt.serviceDisabled,
91104
getServiceStatusFails: tt.getProjectFails,
92105
getServiceStatusResp: tt.getProjectResp,
93106
}

0 commit comments

Comments
 (0)