Skip to content

Commit 0a8f84b

Browse files
authored
feat(redis): Add wait handlers for v2 package (#8248)
relates to STACKITSDK-477
1 parent b35ea68 commit 0a8f84b

7 files changed

Lines changed: 509 additions & 17 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121
- **New**: STACKIT Model Experiments module wait handler added.
2222
- `rabbitmq`:
2323
- [v1.1.0](services/rabbitmq/CHANGELOG.md#v110)
24-
- `v2api`:
25-
- **Feature**: Added wait handlers
24+
- `v2api`:
25+
- **Feature**: Added wait handlers
26+
- `redis`:
27+
- [v1.1.0](services/redis/CHANGELOG.md#v110)
28+
- `v2api`:
29+
- **Feature**: Added wait handlers
2630
- `ske`:
2731
- [v1.19.0](services/ske/CHANGELOG.md#v1190)
2832
- Package `v1api`:

examples/redis/go.mod

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@ go 1.25
55
// This is not needed in production. This is only here to point the golangci linter to the local version instead of the last release on GitHub.
66
replace github.com/stackitcloud/stackit-sdk-go/services/redis => ../../services/redis
77

8-
require (
9-
github.com/stackitcloud/stackit-sdk-go/core v0.26.0
10-
github.com/stackitcloud/stackit-sdk-go/services/redis v0.30.0
11-
)
8+
require github.com/stackitcloud/stackit-sdk-go/services/redis v1.1.0
129

1310
require (
1411
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
1512
github.com/google/uuid v1.6.0 // indirect
13+
github.com/stackitcloud/stackit-sdk-go/core v0.26.0 // indirect
1614
)

examples/redis/redis.go

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,33 +5,32 @@ import (
55
"fmt"
66
"os"
77

8-
"github.com/stackitcloud/stackit-sdk-go/core/config"
9-
redis "github.com/stackitcloud/stackit-sdk-go/services/redis/v1api"
8+
redis "github.com/stackitcloud/stackit-sdk-go/services/redis/v2api"
9+
"github.com/stackitcloud/stackit-sdk-go/services/redis/v2api/wait"
1010
)
1111

1212
func main() {
1313
projectId := "PROJECT_ID" // the uuid of your STACKIT project
1414
planId := "PLAN_ID"
15+
region := "eu01"
1516

1617
// Create a new API client, that uses default authentication and configuration
17-
redisClient, err := redis.NewAPIClient(
18-
config.WithRegion("eu01"),
19-
)
18+
redisClient, err := redis.NewAPIClient()
2019
if err != nil {
2120
fmt.Fprintf(os.Stderr, "Creating API client: %v\n", err)
2221
os.Exit(1)
2322
}
2423

2524
// Get the redis instances for your project
26-
getInstancesResp, err := redisClient.DefaultAPI.ListInstances(context.Background(), projectId).Execute()
25+
getInstancesResp, err := redisClient.DefaultAPI.ListInstances(context.Background(), projectId, region).Execute()
2726
if err != nil {
2827
fmt.Fprintf(os.Stderr, "Error when calling `GetInstances`: %v\n", err)
2928
} else {
3029
fmt.Printf("Number of instances: %v\n", len(getInstancesResp.Instances))
3130
}
3231

3332
// Get the redis offerings for your project
34-
getOfferingsResp, err := redisClient.DefaultAPI.ListOfferings(context.Background(), projectId).Execute()
33+
getOfferingsResp, err := redisClient.DefaultAPI.ListOfferings(context.Background(), projectId, region).Execute()
3534
if err != nil {
3635
fmt.Fprintf(os.Stderr, "Error when calling `GetOfferings`: %v\n", err)
3736
} else {
@@ -44,10 +43,34 @@ func main() {
4443
Parameters: &redis.InstanceParameters{},
4544
PlanId: planId,
4645
}
47-
createInstanceResp, err := redisClient.DefaultAPI.CreateInstance(context.Background(), projectId).CreateInstancePayload(createInstancePayload).Execute()
46+
createInstanceResp, err := redisClient.DefaultAPI.CreateInstance(context.Background(), projectId, region).CreateInstancePayload(createInstancePayload).Execute()
4847
if err != nil {
4948
fmt.Fprintf(os.Stderr, "Error when calling `CreateInstance`: %v\n", err)
50-
} else {
51-
fmt.Printf("Created instance with instance id \"%s\".\n", createInstanceResp.InstanceId)
49+
os.Exit(1)
50+
}
51+
fmt.Printf("Triggered creation of instance with instance id \"%s\".\n", createInstanceResp.InstanceId)
52+
53+
// Wait for creation of redis instance
54+
instance, err := wait.CreateInstanceWaitHandler(context.Background(), redisClient.DefaultAPI, projectId, region, createInstanceResp.InstanceId).WaitWithContext(context.Background())
55+
if err != nil {
56+
fmt.Fprintf(os.Stderr, "Error when waiting for creation: %v\n", err)
57+
os.Exit(1)
58+
}
59+
fmt.Printf("Redis instance %q has been successfully created.\n", *instance.InstanceId)
60+
61+
// Delete a redis instance
62+
err = redisClient.DefaultAPI.DeleteInstance(context.Background(), projectId, region, *instance.InstanceId).Execute()
63+
if err != nil {
64+
fmt.Fprintf(os.Stderr, "Error when calling 'DeleteInstance': %v\n", err)
65+
os.Exit(1)
66+
}
67+
fmt.Printf("Deleting instance with instance id %q.\n", createInstanceResp.InstanceId)
68+
69+
// Wait for deletion of redis instance
70+
_, err = wait.DeleteInstanceWaitHandler(context.Background(), redisClient.DefaultAPI, projectId, region, *instance.InstanceId).WaitWithContext(context.Background())
71+
if err != nil {
72+
fmt.Fprintf(os.Stderr, "Error when waiting for deletion: %v\n", err)
73+
os.Exit(1)
5274
}
75+
fmt.Printf("Redis instance %q has been successfully deleted.\n", *instance.InstanceId)
5376
}

services/redis/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## v1.1.0
2+
- `v2api`: **Feature**: Added wait handlers
3+
14
## v1.0.0
25
- **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.
36
- `v2api`:

services/redis/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v1.0.0
1+
v1.1.0

services/redis/v2api/wait/wait.go

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package wait
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
"strings"
9+
"time"
10+
11+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
12+
"github.com/stackitcloud/stackit-sdk-go/core/wait"
13+
redis "github.com/stackitcloud/stackit-sdk-go/services/redis/v2api"
14+
)
15+
16+
func createOrUpdateInstanceWaitHandler(ctx context.Context, client redis.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[redis.Instance] {
17+
waitConfig := wait.WaiterHelper[redis.Instance, redis.InstanceStatus]{
18+
FetchInstance: client.GetInstance(ctx, projectId, region, instanceId).Execute,
19+
GetState: func(response *redis.Instance) (redis.InstanceStatus, error) {
20+
if response == nil {
21+
return "", errors.New("empty response")
22+
}
23+
if response.Status == nil {
24+
return "", errors.New("status is missing in response")
25+
}
26+
return *response.Status, nil
27+
},
28+
ActiveState: []redis.InstanceStatus{redis.INSTANCESTATUS_ACTIVE},
29+
ErrorState: []redis.InstanceStatus{redis.INSTANCESTATUS_FAILED},
30+
}
31+
32+
handler := wait.New(waitConfig.Wait())
33+
handler.SetTimeout(45 * time.Minute)
34+
return handler
35+
}
36+
37+
// CreateInstanceWaitHandler will wait for instance creation
38+
func CreateInstanceWaitHandler(ctx context.Context, client redis.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[redis.Instance] {
39+
return createOrUpdateInstanceWaitHandler(ctx, client, projectId, region, instanceId)
40+
}
41+
42+
// PartialUpdateInstanceWaitHandler will wait for instance update
43+
func PartialUpdateInstanceWaitHandler(ctx context.Context, client redis.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[redis.Instance] {
44+
return createOrUpdateInstanceWaitHandler(ctx, client, projectId, region, instanceId)
45+
}
46+
47+
// DeleteInstanceWaitHandler will wait for instance deletion
48+
func DeleteInstanceWaitHandler(ctx context.Context, a redis.DefaultAPI, projectId, region, instanceId string) *wait.AsyncActionHandler[struct{}] {
49+
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
50+
s, err := a.GetInstance(ctx, projectId, region, instanceId).Execute()
51+
if err == nil {
52+
if s.Status == nil {
53+
return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The status is missing", instanceId)
54+
}
55+
if *s.Status != redis.INSTANCESTATUS_DELETING {
56+
return false, nil, nil
57+
}
58+
if *s.Status == redis.INSTANCESTATUS_ACTIVE {
59+
if strings.Contains(s.LastOperation.Description, "DeleteFailed") || strings.Contains(s.LastOperation.Description, "failed") {
60+
return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", s.LastOperation.Description)
61+
}
62+
return true, nil, nil
63+
}
64+
return false, nil, nil
65+
}
66+
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
67+
if !ok {
68+
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
69+
}
70+
if oapiErr.StatusCode != http.StatusGone {
71+
return false, nil, err
72+
}
73+
return true, nil, nil
74+
})
75+
handler.SetTimeout(15 * time.Minute)
76+
return handler
77+
}
78+
79+
// CreateCredentialsWaitHandler will wait for credentials creation
80+
func CreateCredentialsWaitHandler(ctx context.Context, a redis.DefaultAPI, projectId, region, instanceId, credentialsId string) *wait.AsyncActionHandler[redis.CredentialsResponse] {
81+
handler := wait.New(func() (waitFinished bool, response *redis.CredentialsResponse, err error) {
82+
s, err := a.GetCredentials(ctx, projectId, region, instanceId, credentialsId).Execute()
83+
if err != nil {
84+
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
85+
if !ok {
86+
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
87+
}
88+
// If the request returns 404, the credentials have not been created yet
89+
if oapiErr.StatusCode == http.StatusNotFound {
90+
return false, nil, nil
91+
}
92+
return false, nil, err
93+
}
94+
if s.Id == credentialsId {
95+
return true, s, nil
96+
}
97+
return false, nil, nil
98+
})
99+
handler.SetTimeout(1 * time.Minute)
100+
return handler
101+
}
102+
103+
// DeleteCredentialsWaitHandler will wait for credentials deletion
104+
func DeleteCredentialsWaitHandler(ctx context.Context, a redis.DefaultAPI, projectId, region, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] {
105+
handler := wait.New(func() (waitFinished bool, response *struct{}, err error) {
106+
_, err = a.GetCredentials(ctx, projectId, region, instanceId, credentialsId).Execute()
107+
if err == nil {
108+
return false, nil, nil
109+
}
110+
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
111+
if !ok {
112+
return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError")
113+
}
114+
if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone {
115+
return false, nil, err
116+
}
117+
return true, nil, nil
118+
})
119+
handler.SetTimeout(1 * time.Minute)
120+
return handler
121+
}

0 commit comments

Comments
 (0)