From e9c01f04cc865e0b81494f3a91c700ccf2443329 Mon Sep 17 00:00:00 2001 From: Pere Manent Date: Wed, 16 Apr 2025 13:28:13 +0200 Subject: [PATCH 1/2] Add waiters in git service for Create and Delete git instances --- services/git/wait/wait.go | 61 +++++++++ services/git/wait/wait_test.go | 244 +++++++++++++++++++++++++++++++++ 2 files changed, 305 insertions(+) create mode 100644 services/git/wait/wait.go create mode 100644 services/git/wait/wait_test.go diff --git a/services/git/wait/wait.go b/services/git/wait/wait.go new file mode 100644 index 000000000..0a7eb1505 --- /dev/null +++ b/services/git/wait/wait.go @@ -0,0 +1,61 @@ +package wait + +import ( + "context" + "fmt" + "time" + + "github.com/stackitcloud/stackit-sdk-go/core/wait" + "github.com/stackitcloud/stackit-sdk-go/services/git" +) + +const ( + CreateSuccess = "Ready" + Creating = "Creating" + CreateFail = "Error" +) + +// APIClientInterface Interfaces needed for tests +type APIClientInterface interface { + GetGitExecute(ctx context.Context, projectId string, instanceId string) (*git.Instance, error) +} + +func CreateGitInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[git.Instance] { + handler := wait.New(func() (waitFinished bool, response *git.Instance, err error) { + //crete the handler of the async action + instance, err := a.GetGitExecute(ctx, projectId, instanceId) + if err != nil { + return false, nil, err + } + //Check if the instance.Id is empty + if *instance.Id == "" || *instance.State == "" { + return false, nil, fmt.Errorf("could not get Instance id or State from response for project %s and instanceId %s", projectId, instanceId) + } + if *instance.Id == instanceId && *instance.State == CreateSuccess { + return true, instance, nil + } + if *instance.Id == instanceId && *instance.State == CreateFail { + return true, instance, fmt.Errorf("create failed for Instance with id %s", instanceId) + } + return false, nil, nil + }) + handler.SetTimeout(10 * time.Minute) + return handler +} + +func DeleteGitInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[git.Instance] { + handler := wait.New(func() (waitFinished bool, response *git.Instance, err error) { + //create the handler of the async action + instance, err := a.GetGitExecute(ctx, projectId, instanceId) + if err != nil { + return true, nil, err + } + if instance != nil { + return true, instance, nil + } + //If there is no instance, it means it was deleted successfully + return true, nil, nil + }) + handler.SetTimeout(10 * time.Minute) + return handler +} diff --git a/services/git/wait/wait_test.go b/services/git/wait/wait_test.go new file mode 100644 index 000000000..611b6bc46 --- /dev/null +++ b/services/git/wait/wait_test.go @@ -0,0 +1,244 @@ +package wait + +import ( + "context" + "net/http" + "testing" + "time" + + "github.com/google/go-cmp/cmp" + "github.com/google/uuid" + "github.com/stackitcloud/stackit-sdk-go/core/oapierror" + "github.com/stackitcloud/stackit-sdk-go/core/utils" + "github.com/stackitcloud/stackit-sdk-go/services/git" +) + +type apiClientMocked struct { + desc string + getFails bool + returnInstance bool + wantErr bool + created time.Time + id string + projectId string + name string + state string + url string + version string +} + +func (a *apiClientMocked) GetGitExecute(_ context.Context, _ string, _ string) (*git.Instance, error) { + if a.getFails { + return nil, &oapierror.GenericOpenAPIError{ + StatusCode: http.StatusInternalServerError, + } + } + if !a.returnInstance { + return nil, nil + } + return &git.Instance{ + Created: utils.Ptr(a.created), + Id: utils.Ptr(a.id), + Name: utils.Ptr(a.name), + State: utils.Ptr(a.state), + Url: utils.Ptr(a.url), + Version: utils.Ptr(a.version), + }, nil + +} + +func TestCreateGitInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + getFails bool + wantErr bool + wantResp bool + created time.Time + id string + projectId string + name string + state string + url string + version string + returnInstance bool + }{ + { + desc: "Creation of an instance succeeded", + getFails: false, + wantErr: false, + wantResp: true, + created: time.Now(), + id: uuid.New().String(), + projectId: uuid.New().String(), + name: "instance-test", + state: CreateSuccess, + url: "https://testing.git.onstackit.cloud", + version: "v1.6.0", + returnInstance: true, + }, + { + desc: "Creation of an instance Failed With Error", + getFails: true, + wantErr: true, + wantResp: false, + created: time.Now(), + id: uuid.New().String(), + projectId: uuid.New().String(), + name: "instance-test", + state: CreateFail, + url: "https://testing.git.onstackit.cloud", + version: "v1.6.0", + returnInstance: true, + }, + { + desc: "Creation of an instance with response failed and without error", + getFails: false, + wantErr: true, + wantResp: true, + created: time.Now(), + id: uuid.New().String(), + projectId: uuid.New().String(), + name: "instance-test", + state: CreateFail, + url: "https://testing.git.onstackit.cloud", + version: "v1.6.0", + returnInstance: true, + }, + { + desc: "Creation of an instance failed without id on the response", + getFails: false, + wantErr: true, + wantResp: false, + created: time.Now(), + projectId: uuid.New().String(), + name: "instance-test", + state: CreateFail, + url: "https://testing.git.onstackit.cloud", + version: "v1.6.0", + returnInstance: true, + }, + + { + desc: "Creation of an instance without state on the response", + getFails: false, + wantErr: true, + wantResp: false, + created: time.Now(), + id: uuid.New().String(), + projectId: uuid.New().String(), + name: "instance-test", + url: "https://testing.git.onstackit.cloud", + version: "v1.6.0", + returnInstance: true, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + apiClient := &apiClientMocked{ + desc: tt.desc, + getFails: tt.getFails, + wantErr: tt.wantErr, + created: tt.created, + id: tt.id, + projectId: tt.projectId, + name: tt.name, + state: tt.state, + url: tt.url, + version: tt.version, + returnInstance: tt.returnInstance, + } + var instanceWanted *git.Instance + if tt.wantResp { + instanceWanted = &git.Instance{ + Created: utils.Ptr(tt.created), + Id: utils.Ptr(tt.id), + Name: utils.Ptr(tt.name), + State: utils.Ptr(tt.state), + Url: utils.Ptr(tt.url), + Version: utils.Ptr(tt.version), + } + } + + handler := CreateGitInstanceWaitHandler(context.Background(), apiClient, apiClient.projectId, apiClient.id) + + response, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if !cmp.Equal(response, instanceWanted) { + t.Fatalf("handler gotRes = %v, want %v", response, instanceWanted) + } + }) + } +} + +// Create a function TestDeleteGitInstanceWaitHandler + +func TestDeleteGitInstanceWaitHandler(t *testing.T) { + tests := []struct { + desc string + wantErr bool + wantReturnedInstance bool + getFails bool + returnInstance bool + created time.Time + id string + name string + state string + url string + version string + }{ + { + desc: "Instance deletion failed with error", + wantErr: true, + getFails: true, + }, + { + desc: "Instance deletion failed returning existing instance", + wantErr: false, + getFails: false, + wantReturnedInstance: true, + created: time.Now(), + id: uuid.New().String(), + name: "instance-test", + state: CreateSuccess, + returnInstance: true, + url: "https://testing.git.onstackit.cloud", + version: "v1.6.0", + }, + { + desc: "Instance deletion succesfull", + wantErr: false, + getFails: false, + wantReturnedInstance: false, + returnInstance: false, + }, + } + for _, tt := range tests { + t.Run(tt.desc, func(t *testing.T) { + apiClient := &apiClientMocked{ + + projectId: uuid.New().String(), + getFails: tt.getFails, + created: tt.created, + id: tt.id, + name: tt.name, + state: tt.state, + url: tt.url, + version: tt.version, + returnInstance: tt.returnInstance, + } + + handler := DeleteGitInstanceWaitHandler(context.Background(), apiClient, apiClient.projectId, apiClient.id) + response, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + + if (err != nil) != tt.wantErr { + t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) + } + if (response != nil) != tt.wantReturnedInstance { + t.Fatalf("handler gotRes = %v, want nil", response) + } + }) + } +} From 894ebe47b85e6e94eacff817cd2e54c6e9c1a7f7 Mon Sep 17 00:00:00 2001 From: Pere Manent Date: Wed, 16 Apr 2025 13:37:13 +0200 Subject: [PATCH 2/2] Add waiters in git service for Create and Delete git instances --- services/git/go.mod | 1 + services/git/wait/wait.go | 4 ---- services/git/wait/wait_test.go | 5 +---- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/services/git/go.mod b/services/git/go.mod index 2b391788a..53d00a814 100644 --- a/services/git/go.mod +++ b/services/git/go.mod @@ -3,6 +3,7 @@ module github.com/stackitcloud/stackit-sdk-go/services/git go 1.21 require ( + github.com/google/go-cmp v0.7.0 github.com/google/uuid v1.6.0 github.com/stackitcloud/stackit-sdk-go/core v0.17.1 ) diff --git a/services/git/wait/wait.go b/services/git/wait/wait.go index 0a7eb1505..37fce44f3 100644 --- a/services/git/wait/wait.go +++ b/services/git/wait/wait.go @@ -22,12 +22,10 @@ type APIClientInterface interface { func CreateGitInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[git.Instance] { handler := wait.New(func() (waitFinished bool, response *git.Instance, err error) { - //crete the handler of the async action instance, err := a.GetGitExecute(ctx, projectId, instanceId) if err != nil { return false, nil, err } - //Check if the instance.Id is empty if *instance.Id == "" || *instance.State == "" { return false, nil, fmt.Errorf("could not get Instance id or State from response for project %s and instanceId %s", projectId, instanceId) } @@ -45,7 +43,6 @@ func CreateGitInstanceWaitHandler(ctx context.Context, a APIClientInterface, pro func DeleteGitInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[git.Instance] { handler := wait.New(func() (waitFinished bool, response *git.Instance, err error) { - //create the handler of the async action instance, err := a.GetGitExecute(ctx, projectId, instanceId) if err != nil { return true, nil, err @@ -53,7 +50,6 @@ func DeleteGitInstanceWaitHandler(ctx context.Context, a APIClientInterface, pro if instance != nil { return true, instance, nil } - //If there is no instance, it means it was deleted successfully return true, nil, nil }) handler.SetTimeout(10 * time.Minute) diff --git a/services/git/wait/wait_test.go b/services/git/wait/wait_test.go index 611b6bc46..e1011c67e 100644 --- a/services/git/wait/wait_test.go +++ b/services/git/wait/wait_test.go @@ -27,7 +27,7 @@ type apiClientMocked struct { version string } -func (a *apiClientMocked) GetGitExecute(_ context.Context, _ string, _ string) (*git.Instance, error) { +func (a *apiClientMocked) GetGitExecute(_ context.Context, _, _ string) (*git.Instance, error) { if a.getFails { return nil, &oapierror.GenericOpenAPIError{ StatusCode: http.StatusInternalServerError, @@ -44,7 +44,6 @@ func (a *apiClientMocked) GetGitExecute(_ context.Context, _ string, _ string) ( Url: utils.Ptr(a.url), Version: utils.Ptr(a.version), }, nil - } func TestCreateGitInstanceWaitHandler(t *testing.T) { @@ -173,8 +172,6 @@ func TestCreateGitInstanceWaitHandler(t *testing.T) { } } -// Create a function TestDeleteGitInstanceWaitHandler - func TestDeleteGitInstanceWaitHandler(t *testing.T) { tests := []struct { desc string