Skip to content

Commit 09252ea

Browse files
authored
Onboard PostgreSQL Flex force delete (#194)
* update create examples to existing flavor * add the --force flag to delete command * generate docs * add forcedeleteinstance waiter * extract deletion logic to function, test it. Address PR comments * fix linting, generate docs * rename util function * update sdk version
1 parent 3cc4fd7 commit 09252ea

File tree

9 files changed

+325
-27
lines changed

9 files changed

+325
-27
lines changed

docs/stackit_postgresflex_instance_create.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ stackit postgresflex instance create [flags]
1414

1515
```
1616
Create a PostgreSQL Flex instance with name "my-instance", ACL 0.0.0.0/0 (open access) and specify flavor by CPU and RAM. Other parameters are set to default values
17-
$ stackit postgresflex instance create --name my-instance --cpu 1 --ram 4 --acl 0.0.0.0/0
17+
$ stackit postgresflex instance create --name my-instance --cpu 2 --ram 4 --acl 0.0.0.0/0
1818
1919
Create a PostgreSQL Flex instance with name "my-instance", ACL 0.0.0.0/0 (open access) and specify flavor by ID. Other parameters are set to default values
2020
$ stackit postgresflex instance create --name my-instance --flavor-id xxx --acl 0.0.0.0/0
2121
2222
Create a PostgreSQL Flex instance with name "my-instance", allow access to a specific range of IP addresses, specify flavor by CPU and RAM and set storage size to 20 GB. Other parameters are set to default values
23-
$ stackit postgresflex instance create --name my-instance --cpu 1 --ram 4 --acl 1.2.3.0/24 --storage-size 20
23+
$ stackit postgresflex instance create --name my-instance --cpu 2 --ram 4 --acl 1.2.3.0/24 --storage-size 20
2424
```
2525

2626
### Options

docs/stackit_postgresflex_instance_delete.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ Deletes a PostgreSQL Flex instance
55
### Synopsis
66

77
Deletes a PostgreSQL Flex instance.
8+
By default, instances will be kept in a delayed deleted state for 7 days before being permanently deleted.
9+
Use the --force flag to force the immediate deletion of a delayed deleted instance.
810

911
```
1012
stackit postgresflex instance delete INSTANCE_ID [flags]
@@ -15,12 +17,16 @@ stackit postgresflex instance delete INSTANCE_ID [flags]
1517
```
1618
Delete a PostgreSQL Flex instance with ID "xxx"
1719
$ stackit postgresflex instance delete xxx
20+
21+
Force the deletion of a delayed deleted PostgreSQL Flex instance with ID "xxx"
22+
$ stackit postgresflex instance delete xxx --force
1823
```
1924

2025
### Options
2126

2227
```
23-
-h, --help Help for "stackit postgresflex instance delete"
28+
-f, --force Force deletion of a delayed deleted instance
29+
-h, --help Help for "stackit postgresflex instance delete"
2430
```
2531

2632
### Options inherited from parent commands

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ require (
1515
github.com/stackitcloud/stackit-sdk-go/services/dns v0.8.4
1616
github.com/stackitcloud/stackit-sdk-go/services/mongodbflex v0.11.1
1717
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.10.1
18-
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.11.0
18+
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.12.0
1919
github.com/stackitcloud/stackit-sdk-go/services/resourcemanager v0.7.7
2020
github.com/stackitcloud/stackit-sdk-go/services/secretsmanager v0.6.0
2121
github.com/stackitcloud/stackit-sdk-go/services/serviceaccount v0.3.6

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ github.com/stackitcloud/stackit-sdk-go/services/objectstorage v0.8.6 h1:+mcoBKs6
9191
github.com/stackitcloud/stackit-sdk-go/services/objectstorage v0.8.6/go.mod h1:W9BML8bqZb2dOZe1K+M+qBBs8/QNirr3jA0xxy9tNRY=
9292
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.10.1 h1:LKic8dXtXKsRst2+wY9dNjjkMyJ05QIDpOJuRmVb410=
9393
github.com/stackitcloud/stackit-sdk-go/services/opensearch v0.10.1/go.mod h1:g1o1bmqtTliy9UkFlRV/6bn6GQk+hkvnny3UjMI69S0=
94-
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.11.0 h1:w3vUPJcPE81nItkkbPs1pxm+QF4c0YIbPyY0dd6qI2w=
95-
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.11.0/go.mod h1:P0YyvgwIsVKJijdWGVJVOp/ac7PVX99Oj+dr4v1zECc=
94+
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.12.0 h1:W2WSYUyhKaHQ+BZfmyRw9PKv5q7ihGRyNhNgIlyM+Y8=
95+
github.com/stackitcloud/stackit-sdk-go/services/postgresflex v0.12.0/go.mod h1:P0YyvgwIsVKJijdWGVJVOp/ac7PVX99Oj+dr4v1zECc=
9696
github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.10.0 h1:Fle394socpyf662g3jMrtZpZaWVgBMBIEFnh4fnGock=
9797
github.com/stackitcloud/stackit-sdk-go/services/rabbitmq v0.10.0/go.mod h1:JvqOSrTCiynS0x6Y9OsK54yvdB6AtIWLwXDEjoCkAIg=
9898
github.com/stackitcloud/stackit-sdk-go/services/redis v0.10.1 h1:/tRad17HUcGRm448l8XyX6uhnnHVfj3VdUQquIwNq2Q=

internal/cmd/postgresflex/instance/create/create.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ func NewCmd() *cobra.Command {
6464
Example: examples.Build(
6565
examples.NewExample(
6666
`Create a PostgreSQL Flex instance with name "my-instance", ACL 0.0.0.0/0 (open access) and specify flavor by CPU and RAM. Other parameters are set to default values`,
67-
`$ stackit postgresflex instance create --name my-instance --cpu 1 --ram 4 --acl 0.0.0.0/0`),
67+
`$ stackit postgresflex instance create --name my-instance --cpu 2 --ram 4 --acl 0.0.0.0/0`),
6868
examples.NewExample(
6969
`Create a PostgreSQL Flex instance with name "my-instance", ACL 0.0.0.0/0 (open access) and specify flavor by ID. Other parameters are set to default values`,
7070
`$ stackit postgresflex instance create --name my-instance --flavor-id xxx --acl 0.0.0.0/0`),
7171
examples.NewExample(
7272
`Create a PostgreSQL Flex instance with name "my-instance", allow access to a specific range of IP addresses, specify flavor by CPU and RAM and set storage size to 20 GB. Other parameters are set to default values`,
73-
`$ stackit postgresflex instance create --name my-instance --cpu 1 --ram 4 --acl 1.2.3.0/24 --storage-size 20`),
73+
`$ stackit postgresflex instance create --name my-instance --cpu 2 --ram 4 --acl 1.2.3.0/24 --storage-size 20`),
7474
),
7575
RunE: func(cmd *cobra.Command, args []string) error {
7676
ctx := context.Background()

internal/cmd/postgresflex/instance/delete/delete.go

Lines changed: 94 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"github.com/stackitcloud/stackit-cli/internal/pkg/confirm"
99
"github.com/stackitcloud/stackit-cli/internal/pkg/errors"
1010
"github.com/stackitcloud/stackit-cli/internal/pkg/examples"
11+
"github.com/stackitcloud/stackit-cli/internal/pkg/flags"
1112
"github.com/stackitcloud/stackit-cli/internal/pkg/globalflags"
1213
"github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/client"
1314
postgresflexUtils "github.com/stackitcloud/stackit-cli/internal/pkg/services/postgresflex/utils"
@@ -21,23 +22,33 @@ import (
2122

2223
const (
2324
instanceIdArg = "INSTANCE_ID"
25+
26+
forceDeleteFlag = "force"
2427
)
2528

2629
type inputModel struct {
2730
*globalflags.GlobalFlagModel
28-
InstanceId string
31+
InstanceId string
32+
ForceDelete bool
2933
}
3034

3135
func NewCmd() *cobra.Command {
3236
cmd := &cobra.Command{
3337
Use: fmt.Sprintf("delete %s", instanceIdArg),
3438
Short: "Deletes a PostgreSQL Flex instance",
35-
Long: "Deletes a PostgreSQL Flex instance.",
36-
Args: args.SingleArg(instanceIdArg, utils.ValidateUUID),
39+
Long: fmt.Sprintf("%s\n%s\n%s",
40+
"Deletes a PostgreSQL Flex instance.",
41+
"By default, instances will be kept in a delayed deleted state for 7 days before being permanently deleted.",
42+
"Use the --force flag to force the immediate deletion of a delayed deleted instance.",
43+
),
44+
Args: args.SingleArg(instanceIdArg, utils.ValidateUUID),
3745
Example: examples.Build(
3846
examples.NewExample(
3947
`Delete a PostgreSQL Flex instance with ID "xxx"`,
4048
"$ stackit postgresflex instance delete xxx"),
49+
examples.NewExample(
50+
`Force the deletion of a delayed deleted PostgreSQL Flex instance with ID "xxx"`,
51+
"$ stackit postgresflex instance delete xxx --force"),
4152
),
4253
RunE: func(cmd *cobra.Command, args []string) error {
4354
ctx := context.Background()
@@ -65,35 +76,74 @@ func NewCmd() *cobra.Command {
6576
}
6677
}
6778

68-
// Call API
69-
req := buildRequest(ctx, model, apiClient)
70-
err = req.Execute()
79+
toDelete, toForceDelete, err := getNextOperations(ctx, model, apiClient)
7180
if err != nil {
72-
return fmt.Errorf("delete PostgreSQL Flex instance: %w", err)
81+
return err
7382
}
7483

75-
// Wait for async operation, if async mode not enabled
76-
if !model.Async {
77-
s := spinner.New(cmd)
78-
s.Start("Deleting instance")
79-
_, err = wait.DeleteInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx)
84+
if toDelete {
85+
// Call API
86+
delReq := buildDeleteRequest(ctx, model, apiClient)
87+
err = delReq.Execute()
8088
if err != nil {
81-
return fmt.Errorf("wait for PostgreSQL Flex instance deletion: %w", err)
89+
return fmt.Errorf("delete PostgreSQL Flex instance: %w", err)
90+
}
91+
92+
// Wait for async operation, if async mode not enabled
93+
if !model.Async {
94+
s := spinner.New(cmd)
95+
s.Start("Deleting instance")
96+
_, err = wait.DeleteInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx)
97+
if err != nil {
98+
return fmt.Errorf("wait for PostgreSQL Flex instance deletion: %w", err)
99+
}
100+
s.Stop()
101+
}
102+
}
103+
104+
if toForceDelete {
105+
// Call API
106+
forceDelReq := buildForceDeleteRequest(ctx, model, apiClient)
107+
err = forceDelReq.Execute()
108+
if err != nil {
109+
return fmt.Errorf("force delete PostgreSQL Flex instance: %w", err)
110+
}
111+
112+
// Wait for async operation, if async mode not enabled
113+
if !model.Async {
114+
s := spinner.New(cmd)
115+
s.Start("Forcing deletion of instance")
116+
_, err = wait.ForceDeleteInstanceWaitHandler(ctx, apiClient, model.ProjectId, model.InstanceId).WaitWithContext(ctx)
117+
if err != nil {
118+
return fmt.Errorf("wait for PostgreSQL Flex instance force deletion: %w", err)
119+
}
120+
s.Stop()
82121
}
83-
s.Stop()
84122
}
85123

86124
operationState := "Deleted"
125+
if toForceDelete {
126+
operationState = "Forcefully deleted"
127+
}
87128
if model.Async {
88129
operationState = "Triggered deletion of"
130+
if toForceDelete {
131+
operationState = "Triggered forced deletion of"
132+
}
89133
}
134+
90135
cmd.Printf("%s instance %q\n", operationState, instanceLabel)
91136
return nil
92137
},
93138
}
139+
configureFlags(cmd)
94140
return cmd
95141
}
96142

143+
func configureFlags(cmd *cobra.Command) {
144+
cmd.Flags().BoolP(forceDeleteFlag, "f", false, "Force deletion of a delayed deleted instance")
145+
}
146+
97147
func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
98148
instanceId := inputArgs[0]
99149

@@ -105,10 +155,39 @@ func parseInput(cmd *cobra.Command, inputArgs []string) (*inputModel, error) {
105155
return &inputModel{
106156
GlobalFlagModel: globalFlags,
107157
InstanceId: instanceId,
158+
ForceDelete: flags.FlagToBoolValue(cmd, forceDeleteFlag),
108159
}, nil
109160
}
110161

111-
func buildRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiDeleteInstanceRequest {
162+
func buildDeleteRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiDeleteInstanceRequest {
112163
req := apiClient.DeleteInstance(ctx, model.ProjectId, model.InstanceId)
113164
return req
114165
}
166+
167+
func buildForceDeleteRequest(ctx context.Context, model *inputModel, apiClient *postgresflex.APIClient) postgresflex.ApiForceDeleteInstanceRequest {
168+
req := apiClient.ForceDeleteInstance(ctx, model.ProjectId, model.InstanceId)
169+
return req
170+
}
171+
172+
type PostgreSQLFlexClient interface {
173+
GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*postgresflex.InstanceResponse, error)
174+
ListVersionsExecute(ctx context.Context, projectId string) (*postgresflex.ListVersionsResponse, error)
175+
GetUserExecute(ctx context.Context, projectId, instanceId, userId string) (*postgresflex.GetUserResponse, error)
176+
}
177+
178+
func getNextOperations(ctx context.Context, model *inputModel, apiClient PostgreSQLFlexClient) (toDelete, toForceDelete bool, err error) {
179+
instanceStatus, err := postgresflexUtils.GetInstanceStatus(ctx, apiClient, model.ProjectId, model.InstanceId)
180+
if err != nil {
181+
return false, false, fmt.Errorf("get PostgreSQL Flex instance status: %w", err)
182+
}
183+
184+
if instanceStatus == wait.InstanceStateDeleted {
185+
if !model.ForceDelete {
186+
return false, false, fmt.Errorf("instance is already deleted, use --force to force the deletion of a delayed deleted instance")
187+
}
188+
189+
return false, model.ForceDelete, nil
190+
}
191+
192+
return true, model.ForceDelete, nil
193+
}

0 commit comments

Comments
 (0)