diff --git a/CHANGELOG.md b/CHANGELOG.md index 1823cf28f..7e766e384 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,6 +32,10 @@ - **Breaking Change:** `Value` field of `Label`/`CreateLabelPayload` is no longer required. `NewLabel`/`NewCreateLabelPayload` drop the `value` param - `v1api`: - **Breaking Change:** `Value` field of `Label`/`CreateLabelPayload` changed from `string` to `*string` and is no longer required. `NewLabel`/`NewCreateLabelPayload` drop the `value` param +- `dremio`: + - [v0.5.0](services/dremio/CHANGELOG.md#v050) + - `v1beta`: + - Add support for the new api version - `logme`: - [v1.2.0](services/logme/CHANGELOG.md#v120) - **Breaking Change/Fix:** `ListBackups` operation now returns `[]Backup` instead of `*ListBackupsResponse` (response type also removed) diff --git a/examples/dremio/dremio.go b/examples/dremio/dremio.go index 8d58dde7f..7f022eff5 100644 --- a/examples/dremio/dremio.go +++ b/examples/dremio/dremio.go @@ -7,8 +7,8 @@ import ( "github.com/stackitcloud/stackit-sdk-go/core/config" "github.com/stackitcloud/stackit-sdk-go/core/utils" - dremio "github.com/stackitcloud/stackit-sdk-go/services/dremio/v1alphaapi" - "github.com/stackitcloud/stackit-sdk-go/services/dremio/v1alphaapi/wait" + dremio "github.com/stackitcloud/stackit-sdk-go/services/dremio/v1betaapi" + "github.com/stackitcloud/stackit-sdk-go/services/dremio/v1betaapi/wait" ) func main() { diff --git a/services/dremio/CHANGELOG.md b/services/dremio/CHANGELOG.md index 6ebac93bb..bd2c014d9 100644 --- a/services/dremio/CHANGELOG.md +++ b/services/dremio/CHANGELOG.md @@ -1,3 +1,6 @@ +## v0.5.0 +- Add support for v1beta version of the API + ## v0.4.0 - `v1alphaapi`: Align package to latest API specification @@ -11,4 +14,4 @@ - Manage your STACKIT Dremio resources: `DremioInstance`, `DremioUser` - Waiters for async operations: `CreateDremioInstanceWaitHandler`, `UpdateDremioInstanceWaitHandler`, `DeleteDremioInstanceWaitHandler`, `CreateDremioUserWaitHandler`, `UpdateDremioUserWaitHandler`, `DeleteDremioUserWaitHandler` -- [Usage example](https://github.com/stackitcloud/stackit-sdk-go/tree/main/examples/dremio) \ No newline at end of file +- [Usage example](https://github.com/stackitcloud/stackit-sdk-go/tree/main/examples/dremio) diff --git a/services/dremio/VERSION b/services/dremio/VERSION index 01e994d3d..b043aa648 100644 --- a/services/dremio/VERSION +++ b/services/dremio/VERSION @@ -1 +1 @@ -v0.4.0 \ No newline at end of file +v0.5.0 diff --git a/services/dremio/v1betaapi/wait/wait.go b/services/dremio/v1betaapi/wait/wait.go new file mode 100644 index 000000000..9ea47ec52 --- /dev/null +++ b/services/dremio/v1betaapi/wait/wait.go @@ -0,0 +1,141 @@ +package wait + +import ( + "context" + "errors" + "net/http" + "time" + + "github.com/stackitcloud/stackit-sdk-go/core/wait" + dremio "github.com/stackitcloud/stackit-sdk-go/services/dremio/v1betaapi" +) + +const ( + // Deprecated: symbol is not used anymore, use the packages enum instead, will be removed 2026-12, use `go fix` for automatic fixing + //go:fix inline + DREMIOSTATE_ACTIVE = dremio.DREMIORESPONSESTATE_ACTIVE + // Deprecated: symbol is not used anymore, use the packages enum instead, will be removed 2026-12, use `go fix` for automatic fixing + //go:fix inline + DREMIOSTATE_ERROR = dremio.DREMIORESPONSESTATE_ERROR + + // Deprecated: symbol is not used anymore, use the packages enum instead, will be removed 2026-12, use `go fix` for automatic fixing + //go:fix inline + DREMIOUSERSTATE_ACTIVE = dremio.DREMIOUSERRESPONSESTATE_ACTIVE + // Deprecated: symbol is not used anymore, use the packages enum instead, will be removed 2026-12, use `go fix` for automatic fixing + //go:fix inline + DREMIOUSERSTATE_ERROR = dremio.DREMIOUSERRESPONSESTATE_ERROR +) + +// CreateDremioWaitHandler will wait for the creation of a Dremio instance +func CreateDremioWaitHandler(ctx context.Context, a dremio.DefaultAPI, projectId, regionId, dremioId string) *wait.AsyncActionHandler[dremio.DremioResponse] { + waitConfig := wait.WaiterHelper[dremio.DremioResponse, dremio.DremioResponseState]{ + FetchInstance: a.GetDremioInstance(ctx, projectId, regionId, dremioId).Execute, + GetState: func(dremioResp *dremio.DremioResponse) (dremio.DremioResponseState, error) { + if dremioResp == nil { + return "", errors.New("empty response") + } + return dremioResp.State, nil + }, + ActiveState: []dremio.DremioResponseState{dremio.DREMIORESPONSESTATE_ACTIVE}, + ErrorState: []dremio.DremioResponseState{dremio.DREMIORESPONSESTATE_ERROR}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(30 * time.Minute) + return handler +} + +// UpdateDremioWaitHandler will wait an update of a Dremio instance +func UpdateDremioWaitHandler(ctx context.Context, a dremio.DefaultAPI, projectId, regionId, dremioId string) *wait.AsyncActionHandler[dremio.DremioResponse] { + waitConfig := wait.WaiterHelper[dremio.DremioResponse, dremio.DremioResponseState]{ + FetchInstance: a.GetDremioInstance(ctx, projectId, regionId, dremioId).Execute, + GetState: func(dremioResp *dremio.DremioResponse) (dremio.DremioResponseState, error) { + if dremioResp == nil { + return "", errors.New("empty response") + } + return dremioResp.State, nil + }, + ActiveState: []dremio.DremioResponseState{dremio.DREMIORESPONSESTATE_ACTIVE}, + ErrorState: []dremio.DremioResponseState{dremio.DREMIORESPONSESTATE_ERROR}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(30 * time.Minute) + return handler +} + +// DeleteDremioWaitHandler will wait for the deletion of a Dremio instance +func DeleteDremioWaitHandler(ctx context.Context, a dremio.DefaultAPI, projectId, regionId, dremioId string) *wait.AsyncActionHandler[dremio.DremioResponse] { + waitConfig := wait.WaiterHelper[dremio.DremioResponse, dremio.DremioResponseState]{ + FetchInstance: a.GetDremioInstance(ctx, projectId, regionId, dremioId).Execute, + GetState: func(dremioResp *dremio.DremioResponse) (dremio.DremioResponseState, error) { + if dremioResp == nil { + return "", errors.New("empty response") + } + return dremioResp.State, nil + }, + ErrorState: []dremio.DremioResponseState{dremio.DREMIORESPONSESTATE_ERROR}, + DeleteHttpErrorStatusCodes: []int{http.StatusNotFound}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(30 * time.Minute) + return handler +} + +// CreateDremioUserWaitHandler will wait for the creation of a Dremio user +func CreateDremioUserWaitHandler(ctx context.Context, a dremio.DefaultAPI, projectId, regionId, dremioId, dremioUserId string) *wait.AsyncActionHandler[dremio.DremioUserResponse] { + waitConfig := wait.WaiterHelper[dremio.DremioUserResponse, dremio.DremioUserResponseState]{ + FetchInstance: a.GetDremioUser(ctx, projectId, regionId, dremioId, dremioUserId).Execute, + GetState: func(user *dremio.DremioUserResponse) (dremio.DremioUserResponseState, error) { + if user == nil { + return "", errors.New("empty response") + } + return user.State, nil + }, + ActiveState: []dremio.DremioUserResponseState{dremio.DREMIOUSERRESPONSESTATE_ACTIVE}, + ErrorState: []dremio.DremioUserResponseState{dremio.DREMIOUSERRESPONSESTATE_ERROR}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(10 * time.Minute) + return handler +} + +// UpdateDremioUserWaitHandler will wait for an update to a Dremio user +func UpdateDremioUserWaitHandler(ctx context.Context, a dremio.DefaultAPI, projectId, regionId, dremioId, dremioUserId string) *wait.AsyncActionHandler[dremio.DremioUserResponse] { + waitConfig := wait.WaiterHelper[dremio.DremioUserResponse, dremio.DremioUserResponseState]{ + FetchInstance: a.GetDremioUser(ctx, projectId, regionId, dremioId, dremioUserId).Execute, + GetState: func(user *dremio.DremioUserResponse) (dremio.DremioUserResponseState, error) { + if user == nil { + return "", errors.New("empty response") + } + return user.State, nil + }, + ActiveState: []dremio.DremioUserResponseState{dremio.DREMIOUSERRESPONSESTATE_ACTIVE}, + ErrorState: []dremio.DremioUserResponseState{dremio.DREMIOUSERRESPONSESTATE_ERROR}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(10 * time.Minute) + return handler +} + +// DeleteDremioUserWaitHandler will wait for a deletion of a Dremio user +func DeleteDremioUserWaitHandler(ctx context.Context, a dremio.DefaultAPI, projectId, regionId, dremioId, dremioUserId string) *wait.AsyncActionHandler[dremio.DremioUserResponse] { + waitConfig := wait.WaiterHelper[dremio.DremioUserResponse, dremio.DremioUserResponseState]{ + FetchInstance: a.GetDremioUser(ctx, projectId, regionId, dremioId, dremioUserId).Execute, + GetState: func(user *dremio.DremioUserResponse) (dremio.DremioUserResponseState, error) { + if user == nil { + return "", errors.New("empty response") + } + return user.State, nil + }, + ErrorState: []dremio.DremioUserResponseState{dremio.DREMIOUSERRESPONSESTATE_ERROR}, + DeleteHttpErrorStatusCodes: []int{http.StatusNotFound}, + } + + handler := wait.New(waitConfig.Wait()) + handler.SetTimeout(10 * time.Minute) + return handler +} diff --git a/services/dremio/v1betaapi/wait/wait_test.go b/services/dremio/v1betaapi/wait/wait_test.go new file mode 100644 index 000000000..b0747aa11 --- /dev/null +++ b/services/dremio/v1betaapi/wait/wait_test.go @@ -0,0 +1,487 @@ +package wait + +import ( + "context" + "net/http" + "testing" + "testing/synctest" + + "github.com/google/go-cmp/cmp" + + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + dremio "github.com/stackitcloud/stackit-sdk-go/services/dremio/v1betaapi" +) + +type mockSettings struct { + isDeleted bool + getFails bool + resourceState string +} + +func newAPIMock(settings mockSettings) dremio.DefaultAPI { + return &dremio.DefaultAPIServiceMock{ + GetDremioInstanceExecuteMock: utils.Ptr( + func(_ dremio.ApiGetDremioInstanceRequest) (*dremio.DremioResponse, error) { + if settings.isDeleted { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: http.StatusNotFound, + } + } + if settings.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: 500, + } + } + + return &dremio.DremioResponse{ + State: dremio.DremioResponseState(settings.resourceState), + }, nil + }, + ), + GetDremioUserExecuteMock: utils.Ptr( + func(_ dremio.ApiGetDremioUserRequest) (*dremio.DremioUserResponse, error) { + if settings.isDeleted { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: http.StatusNotFound, + } + } + if settings.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: 500, + } + } + + return &dremio.DremioUserResponse{ + State: dremio.DremioUserResponseState(settings.resourceState), + }, nil + }, + ), + } +} + +func TestCreateDremioWaitHandler(t *testing.T) { + tests := []struct { + description string + getFails bool + resourceState string + wantError bool + wantResponse bool + }{ + { + description: "create_succeeded", + getFails: false, + resourceState: string(dremio.DREMIORESPONSESTATE_ACTIVE), + wantError: false, + wantResponse: true, + }, + { + description: "create_failed", + getFails: false, + resourceState: string(dremio.DREMIORESPONSESTATE_ERROR), + wantError: true, + wantResponse: true, + }, + { + description: "get_fails", + getFails: true, + resourceState: "", + wantError: true, + wantResponse: false, + }, + { + description: "timeout", + getFails: false, + resourceState: "ANOTHER STATE", + wantError: true, + wantResponse: false, + }, + } + for _, currentTest := range tests { + t.Run(currentTest.description, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(mockSettings{ + getFails: currentTest.getFails, + resourceState: currentTest.resourceState, + isDeleted: false, + }) + + var expectedResponse *dremio.DremioResponse + if currentTest.wantResponse { + expectedResponse = &dremio.DremioResponse{ + State: dremio.DremioResponseState(currentTest.resourceState), + } + } + + handler := CreateDremioWaitHandler(context.Background(), apiClient, "pid", "zid", "dremioId") + + gotResponse, err := handler.WaitWithContext(context.Background()) + + if (err != nil) != currentTest.wantError { + t.Fatalf("handler error = %v, wantErr %v", err, currentTest.wantError) + } + if !cmp.Equal(gotResponse, expectedResponse) { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + }) + }) + } +} + +func TestUpdateDremioWaitHandler(t *testing.T) { + tests := []struct { + description string + getFails bool + resourceState string + wantError bool + wantResponse bool + }{ + { + description: "update_succeeded", + getFails: false, + resourceState: string(dremio.DREMIORESPONSESTATE_ACTIVE), + wantError: false, + wantResponse: true, + }, + { + description: "update_failed", + getFails: false, + resourceState: string(dremio.DREMIORESPONSESTATE_ERROR), + wantError: true, + wantResponse: true, + }, + { + description: "get_fails", + getFails: true, + resourceState: "", + wantError: true, + wantResponse: false, + }, + { + description: "timeout", + getFails: false, + resourceState: "ANOTHER STATE", + wantError: true, + wantResponse: false, + }, + } + for _, currentTest := range tests { + t.Run(currentTest.description, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(mockSettings{ + getFails: currentTest.getFails, + resourceState: currentTest.resourceState, + isDeleted: false, + }) + + var expectedResponse *dremio.DremioResponse + if currentTest.wantResponse { + expectedResponse = &dremio.DremioResponse{ + State: dremio.DremioResponseState(currentTest.resourceState), + } + } + + handler := UpdateDremioWaitHandler(context.Background(), apiClient, "pid", "zid", "dremioId") + + gotResponse, err := handler.WaitWithContext(context.Background()) + + if (err != nil) != currentTest.wantError { + t.Fatalf("handler error = %v, wantErr %v", err, currentTest.wantError) + } + if !cmp.Equal(gotResponse, expectedResponse) { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + }) + }) + } +} + +func TestDeleteDremioWaitHandler(t *testing.T) { + tests := []struct { + description string + isDeleted bool + getFails bool + resourceState string + wantError bool + wantResponse bool + }{ + { + description: "delete_succeeded", + isDeleted: true, + getFails: false, + resourceState: "", + wantError: false, + wantResponse: false, + }, + { + description: "delete_failed", + isDeleted: false, + getFails: false, + resourceState: string(dremio.DREMIORESPONSESTATE_ERROR), + wantError: true, + wantResponse: true, + }, + { + description: "get_fails", + isDeleted: false, + getFails: true, + resourceState: "", + wantError: true, + wantResponse: false, + }, + { + description: "timeout", + isDeleted: false, + getFails: false, + resourceState: "ANOTHER STATE", + wantError: true, + wantResponse: false, + }, + } + for _, currentTest := range tests { + t.Run(currentTest.description, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(mockSettings{ + getFails: currentTest.getFails, + resourceState: currentTest.resourceState, + isDeleted: currentTest.isDeleted, + }) + + var expectedResponse *dremio.DremioResponse + if currentTest.wantResponse { + expectedResponse = &dremio.DremioResponse{ + State: dremio.DremioResponseState(currentTest.resourceState), + } + } + + handler := DeleteDremioWaitHandler(context.Background(), apiClient, "pid", "zid", "dremioId") + + gotResponse, err := handler.WaitWithContext(context.Background()) + + if (err != nil) != currentTest.wantError { + t.Fatalf("handler error = %v, wantErr %v", err, currentTest.wantError) + } + + if !currentTest.wantResponse && gotResponse != nil { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + if currentTest.wantResponse && !cmp.Equal(gotResponse, expectedResponse) { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + }) + }) + } +} + +func TestCreateDremioUserWaitHandler(t *testing.T) { + tests := []struct { + description string + getFails bool + resourceState string + wantError bool + wantResponse bool + }{ + { + description: "create_succeeded", + getFails: false, + resourceState: string(dremio.DREMIOUSERRESPONSESTATE_ACTIVE), + wantError: false, + wantResponse: true, + }, + { + description: "create_failed", + getFails: false, + resourceState: string(dremio.DREMIOUSERRESPONSESTATE_ERROR), + wantError: true, + wantResponse: true, + }, + { + description: "get_fails", + getFails: true, + resourceState: "", + wantError: true, + wantResponse: false, + }, + { + description: "timeout", + getFails: false, + resourceState: "ANOTHER STATE", + wantError: true, + wantResponse: false, + }, + } + for _, currentTest := range tests { + t.Run(currentTest.description, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(mockSettings{ + getFails: currentTest.getFails, + resourceState: currentTest.resourceState, + isDeleted: false, + }) + + var expectedResponse *dremio.DremioUserResponse + if currentTest.wantResponse { + expectedResponse = &dremio.DremioUserResponse{ + State: dremio.DremioUserResponseState(currentTest.resourceState), + } + } + + handler := CreateDremioUserWaitHandler(context.Background(), apiClient, "pid", "zid", "dremioId", "userId") + + gotResponse, err := handler.WaitWithContext(context.Background()) + + if (err != nil) != currentTest.wantError { + t.Fatalf("handler error = %v, wantErr %v", err, currentTest.wantError) + } + if !cmp.Equal(gotResponse, expectedResponse) { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + }) + }) + } +} + +func TestUpdateDremioUserWaitHandler(t *testing.T) { + tests := []struct { + description string + getFails bool + resourceState string + wantError bool + wantResponse bool + }{ + { + description: "update_succeeded", + getFails: false, + resourceState: string(dremio.DREMIOUSERRESPONSESTATE_ACTIVE), + wantError: false, + wantResponse: true, + }, + { + description: "update_failed", + getFails: false, + resourceState: string(dremio.DREMIOUSERRESPONSESTATE_ERROR), + wantError: true, + wantResponse: true, + }, + { + description: "get_fails", + getFails: true, + resourceState: "", + wantError: true, + wantResponse: false, + }, + { + description: "timeout", + getFails: false, + resourceState: "ANOTHER STATE", + wantError: true, + wantResponse: false, + }, + } + for _, currentTest := range tests { + t.Run(currentTest.description, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(mockSettings{ + getFails: currentTest.getFails, + resourceState: currentTest.resourceState, + isDeleted: false, + }) + + var expectedResponse *dremio.DremioUserResponse + if currentTest.wantResponse { + expectedResponse = &dremio.DremioUserResponse{ + State: dremio.DremioUserResponseState(currentTest.resourceState), + } + } + + handler := UpdateDremioUserWaitHandler(context.Background(), apiClient, "pid", "zid", "dremioId", "userId") + + gotResponse, err := handler.WaitWithContext(context.Background()) + + if (err != nil) != currentTest.wantError { + t.Fatalf("handler error = %v, wantErr %v", err, currentTest.wantError) + } + if !cmp.Equal(gotResponse, expectedResponse) { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + }) + }) + } +} + +func TestDeleteDremioUserWaitHandler(t *testing.T) { + tests := []struct { + description string + isDeleted bool + getFails bool + resourceState string + wantError bool + wantResponse bool + }{ + { + description: "delete_succeeded", + isDeleted: true, + getFails: false, + resourceState: "", + wantError: false, + wantResponse: false, + }, + { + description: "delete_failed", + isDeleted: false, + getFails: false, + resourceState: string(dremio.DREMIOUSERRESPONSESTATE_ERROR), + wantError: true, + wantResponse: true, + }, + { + description: "get_fails", + isDeleted: false, + getFails: true, + resourceState: "", + wantError: true, + wantResponse: false, + }, + { + description: "timeout", + isDeleted: false, + getFails: false, + resourceState: "ANOTHER STATE", + wantError: true, + wantResponse: false, + }, + } + for _, currentTest := range tests { + t.Run(currentTest.description, func(t *testing.T) { + synctest.Test(t, func(t *testing.T) { + apiClient := newAPIMock(mockSettings{ + getFails: currentTest.getFails, + resourceState: currentTest.resourceState, + isDeleted: currentTest.isDeleted, + }) + + var expectedResponse *dremio.DremioUserResponse + if currentTest.wantResponse { + expectedResponse = &dremio.DremioUserResponse{ + State: dremio.DremioUserResponseState(currentTest.resourceState), + } + } + + handler := DeleteDremioUserWaitHandler(context.Background(), apiClient, "pid", "zid", "dremioId", "userId") + + gotResponse, err := handler.WaitWithContext(context.Background()) + + if (err != nil) != currentTest.wantError { + t.Fatalf("handler error = %v, wantErr %v", err, currentTest.wantError) + } + + if !currentTest.wantResponse && gotResponse != nil { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + if currentTest.wantResponse && !cmp.Equal(gotResponse, expectedResponse) { + t.Fatalf("handler gotResponse = %v, expectedResponse = %v", gotResponse, expectedResponse) + } + }) + }) + } +}