Skip to content

Commit ec08953

Browse files
committed
feat(logs) add waiters for logs API
1 parent 59946d1 commit ec08953

File tree

3 files changed

+226
-0
lines changed

3 files changed

+226
-0
lines changed

services/logs/go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/stackitcloud/stackit-sdk-go/services/logs
33
go 1.21
44

55
require (
6+
github.com/google/go-cmp v0.7.0
67
github.com/google/uuid v1.6.0
78
github.com/stackitcloud/stackit-sdk-go/core v0.20.0
89
)

services/logs/wait/wait.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package wait
2+
3+
import (
4+
"context"
5+
"errors"
6+
"fmt"
7+
"net/http"
8+
"time"
9+
10+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
11+
"github.com/stackitcloud/stackit-sdk-go/core/wait"
12+
"github.com/stackitcloud/stackit-sdk-go/services/logs"
13+
)
14+
15+
type APIClientInterface interface {
16+
GetLogsInstanceExecute(ctx context.Context, projectId string, regionId string, instanceId string) (*logs.LogsInstance, error)
17+
}
18+
19+
func CreateLogsInstanceWaitHandler(ctx context.Context, client APIClientInterface, projectID, region, instanceID string) *wait.AsyncActionHandler[logs.LogsInstance] {
20+
handler := wait.New(func() (waitFinished bool, response *logs.LogsInstance, err error) {
21+
instance, err := client.GetLogsInstanceExecute(ctx, projectID, region, instanceID)
22+
if err != nil {
23+
return false, nil, err
24+
}
25+
if instance.Id == nil || instance.Status == nil {
26+
return false, nil, fmt.Errorf("get instance, project: %q, region: %q, instanceID: %q: missing id or status", projectID, region, instanceID)
27+
}
28+
if *instance.Id == instanceID && *instance.Status == logs.LOGSINSTANCESTATUS_ACTIVE {
29+
return true, instance, nil
30+
}
31+
if *instance.Status == logs.LOGSINSTANCESTATUS_DELETING {
32+
return true, nil, fmt.Errorf("creating log instance failed, instance is being deleted")
33+
}
34+
return false, nil, nil
35+
})
36+
handler.SetTimeout(10 * time.Minute)
37+
return handler
38+
}
39+
40+
func DeleteLogsInstanceWaitHandler(ctx context.Context, client APIClientInterface, projectID, region, instanceID string) *wait.AsyncActionHandler[logs.LogsInstance] {
41+
handler := wait.New(func() (waitFinished bool, response *logs.LogsInstance, err error) {
42+
_, err = client.GetLogsInstanceExecute(ctx, projectID, region, instanceID)
43+
// the instances is still gettable, e.g. not deleted, when the errors is null
44+
if err == nil {
45+
return false, nil, nil
46+
}
47+
var oapiError *oapierror.GenericOpenAPIError
48+
if errors.As(err, &oapiError) {
49+
if statusCode := oapiError.StatusCode; statusCode == http.StatusNotFound {
50+
return true, nil, nil
51+
}
52+
}
53+
return false, nil, err
54+
})
55+
handler.SetTimeout(10 * time.Minute)
56+
return handler
57+
}

services/logs/wait/wait_test.go

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
package wait
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"testing"
7+
"time"
8+
9+
"github.com/google/go-cmp/cmp"
10+
"github.com/google/uuid"
11+
"github.com/stackitcloud/stackit-sdk-go/core/oapierror"
12+
"github.com/stackitcloud/stackit-sdk-go/core/utils"
13+
"github.com/stackitcloud/stackit-sdk-go/services/logs"
14+
)
15+
16+
type apiClientMock struct {
17+
getFails bool
18+
returnInstance bool
19+
statusCode int
20+
getLogsResponse *logs.LogsInstance
21+
}
22+
23+
func (a *apiClientMock) GetLogsInstanceExecute(_ context.Context, _, _, _ string) (*logs.LogsInstance, error) {
24+
if a.getFails {
25+
return nil, &oapierror.GenericOpenAPIError{
26+
StatusCode: a.statusCode,
27+
}
28+
}
29+
if !a.returnInstance {
30+
return nil, nil
31+
}
32+
return a.getLogsResponse, nil
33+
}
34+
35+
var PROJECT_ID = uuid.NewString()
36+
var INSTANCE_ID = uuid.NewString()
37+
var REGION = "eu01"
38+
39+
func TestCreateLogsInstanceWaitHandler(t *testing.T) {
40+
tests := []struct {
41+
description string
42+
getFails bool
43+
wantErr bool
44+
wantResp bool
45+
returnInstance bool
46+
getLogsResponse *logs.LogsInstance
47+
}{
48+
{
49+
description: "create succeeded",
50+
getFails: false,
51+
wantErr: false,
52+
wantResp: true,
53+
returnInstance: true,
54+
getLogsResponse: &logs.LogsInstance{
55+
Id: utils.Ptr(INSTANCE_ID),
56+
Status: utils.Ptr(logs.LOGSINSTANCESTATUS_ACTIVE),
57+
},
58+
},
59+
{
60+
description: "create failed with error",
61+
getFails: true,
62+
wantErr: true,
63+
wantResp: false,
64+
returnInstance: true,
65+
getLogsResponse: &logs.LogsInstance{
66+
Id: utils.Ptr(INSTANCE_ID),
67+
Status: utils.Ptr(logs.LOGSINSTANCESTATUS_ACTIVE),
68+
},
69+
},
70+
{
71+
description: "create without id",
72+
getFails: false,
73+
wantErr: true,
74+
wantResp: false,
75+
returnInstance: true,
76+
getLogsResponse: &logs.LogsInstance{
77+
Status: utils.Ptr(logs.LOGSINSTANCESTATUS_ACTIVE),
78+
},
79+
},
80+
{
81+
description: "create without status",
82+
getFails: false,
83+
wantErr: true,
84+
wantResp: false,
85+
returnInstance: true,
86+
getLogsResponse: &logs.LogsInstance{
87+
Id: utils.Ptr(INSTANCE_ID),
88+
},
89+
},
90+
{
91+
description: "instance deleting",
92+
getFails: false,
93+
wantErr: true,
94+
wantResp: false,
95+
returnInstance: true,
96+
getLogsResponse: &logs.LogsInstance{
97+
Id: utils.Ptr(INSTANCE_ID),
98+
Status: utils.Ptr(logs.LOGSINSTANCESTATUS_DELETING),
99+
},
100+
},
101+
}
102+
for _, tt := range tests {
103+
t.Run(tt.description, func(t *testing.T) {
104+
client := &apiClientMock{
105+
getFails: tt.getFails,
106+
getLogsResponse: tt.getLogsResponse,
107+
returnInstance: tt.returnInstance,
108+
}
109+
var instanceWanted *logs.LogsInstance
110+
if tt.wantResp {
111+
instanceWanted = tt.getLogsResponse
112+
}
113+
114+
handler := CreateLogsInstanceWaitHandler(context.Background(), client, PROJECT_ID, REGION, INSTANCE_ID)
115+
116+
response, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
117+
118+
if (err != nil) != tt.wantErr {
119+
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
120+
}
121+
if !cmp.Equal(response, instanceWanted) {
122+
t.Fatalf("handler gotRes = %v, want %v", response, instanceWanted)
123+
}
124+
})
125+
}
126+
}
127+
128+
func TestDeleteLogsInstanceWaitHandler(t *testing.T) {
129+
tests := []struct {
130+
description string
131+
getFails bool
132+
wantErr bool
133+
statusCode int
134+
}{
135+
{
136+
description: "delete succeeded",
137+
getFails: true,
138+
statusCode: http.StatusNotFound,
139+
},
140+
{
141+
description: "delete failed with error",
142+
getFails: true,
143+
wantErr: true,
144+
statusCode: http.StatusInternalServerError,
145+
},
146+
{
147+
description: "delete still in progress",
148+
getFails: false,
149+
wantErr: true,
150+
statusCode: http.StatusOK,
151+
},
152+
}
153+
for _, tt := range tests {
154+
t.Run(tt.description, func(t *testing.T) {
155+
client := &apiClientMock{
156+
getFails: tt.getFails,
157+
returnInstance: false,
158+
statusCode: tt.statusCode,
159+
getLogsResponse: nil,
160+
}
161+
handler := DeleteLogsInstanceWaitHandler(context.Background(), client, PROJECT_ID, REGION, INSTANCE_ID)
162+
_, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background())
163+
if (err != nil) != tt.wantErr {
164+
t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr)
165+
}
166+
})
167+
}
168+
}

0 commit comments

Comments
 (0)