From 0f6d669febc11a3848de369d885da53556ede6b5 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Wed, 3 Jan 2024 04:28:46 -0800 Subject: [PATCH 1/7] test: add unit tests for pkg/client/state --- pkg/client/state_test.go | 146 ++++++++++++++++++++++ test/helpers/prism-go-client/v3/client.go | 53 ++++++++ 2 files changed, 199 insertions(+) create mode 100644 pkg/client/state_test.go create mode 100644 test/helpers/prism-go-client/v3/client.go diff --git a/pkg/client/state_test.go b/pkg/client/state_test.go new file mode 100644 index 0000000000..a583bdb568 --- /dev/null +++ b/pkg/client/state_test.go @@ -0,0 +1,146 @@ +package client + +import ( + "context" + "fmt" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + + nutanixTestClient "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/test/helpers/prism-go-client/v3" +) + +func Test_GetTaskState(t *testing.T) { + client, err := nutanixTestClient.NewTestClient() + assert.NoError(t, err) + // use cleanup over defer as the connection gets closed before the tests run with t.Parallel() + t.Cleanup(func() { + client.Close() + }) + + t.Parallel() + tests := []struct { + name string + taskUUID string + handler func(w http.ResponseWriter, r *http.Request) + ctx context.Context + expectedState string + expectedErr error + }{ + { + name: "succeeded", + taskUUID: "succeeded", + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status": "SUCCEEDED"}`) + }, + ctx: context.Background(), + expectedState: "SUCCEEDED", + }, + { + name: "unauthorized", + taskUUID: "unauthorized", + handler: func(w http.ResponseWriter, r *http.Request) { + http.Error(w, `{"error_code": "401"}`, http.StatusUnauthorized) + }, + ctx: context.Background(), + expectedErr: fmt.Errorf("invalid Nutanix credentials"), + }, + { + name: "invalid", + taskUUID: "invalid", + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status": "INVALID_UUID", "error_detail": "invalid UUID", "progress_message": "invalid UUID"}`) + }, + ctx: context.Background(), + expectedState: "INVALID_UUID", + expectedErr: fmt.Errorf("error_detail: invalid UUID, progress_message: invalid UUID"), + }, + { + name: "failed", + taskUUID: "failed", + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status": "FAILED", "error_detail": "task failed", "progress_message": "will never succeed"}`) + }, + ctx: context.Background(), + expectedState: "FAILED", + expectedErr: fmt.Errorf("error_detail: task failed, progress_message: will never succeed"), + }, + } + for _, tt := range tests { + tt := tt // Capture range variable. + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client.AddHandler(nutanixTestClient.GetTaskURLPath(tt.taskUUID), tt.handler) + + state, err := GetTaskState(tt.ctx, client.Client, tt.taskUUID) + assert.Equal(t, tt.expectedErr, err) + assert.Equal(t, tt.expectedState, state) + }) + } +} + +func Test_WaitForTaskCompletion(t *testing.T) { + client, err := nutanixTestClient.NewTestClient() + assert.NoError(t, err) + // use cleanup over defer as the connection gets closed before the tests run with t.Parallel() + t.Cleanup(func() { + client.Close() + }) + + ctx, cancel := context.WithTimeout(context.Background(), time.Second*1) + t.Cleanup(func() { + cancel() + }) + + t.Parallel() + tests := []struct { + name string + taskUUID string + handler func(w http.ResponseWriter, r *http.Request) + ctx context.Context + expectedErr error + }{ + { + name: "succeeded", + taskUUID: "succeeded", + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status": "SUCCEEDED"}`) + }, + ctx: ctx, + }, + { + name: "invalid", + taskUUID: "invalid", + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status": "INVALID_UUID", "error_detail": "invalid UUID", "progress_message": "invalid UUID"}`) + }, + ctx: ctx, + expectedErr: fmt.Errorf("error_detail: invalid UUID, progress_message: invalid UUID"), + }, + { + name: "timeout", + taskUUID: "timeout", + handler: func(w http.ResponseWriter, r *http.Request) { + fmt.Fprint(w, `{"status": "PENDING"}`) + }, + ctx: ctx, + expectedErr: context.DeadlineExceeded, + }, + } + for _, tt := range tests { + tt := tt // Capture range variable. + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + client.AddHandler(nutanixTestClient.GetTaskURLPath(tt.taskUUID), tt.handler) + + err := WaitForTaskCompletion(tt.ctx, client.Client, tt.taskUUID) + if tt.expectedErr != nil { + assert.ErrorContains(t, err, tt.expectedErr.Error()) + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/test/helpers/prism-go-client/v3/client.go b/test/helpers/prism-go-client/v3/client.go new file mode 100644 index 0000000000..66ec96f112 --- /dev/null +++ b/test/helpers/prism-go-client/v3/client.go @@ -0,0 +1,53 @@ +package v3 + +import ( + "fmt" + + "net/http" + "net/http/httptest" + "path" + + prismgoclient "github.com/nutanix-cloud-native/prism-go-client" + nutanixClientV3 "github.com/nutanix-cloud-native/prism-go-client/v3" +) + +const ( + baseURLPath = "/api/nutanix/v3/" +) + +type TestClient struct { + *nutanixClientV3.Client + + mux *http.ServeMux + server *httptest.Server +} + +func NewTestClient() (*TestClient, error) { + mux := http.NewServeMux() + server := httptest.NewServer(mux) + + cred := prismgoclient.Credentials{ + URL: server.URL, + Username: "username", + Password: "password", + Endpoint: "0.0.0.0", + } + + client, err := nutanixClientV3.NewV3Client(cred) + if err != nil { + return nil, fmt.Errorf("error creating Nutanix test client: %w", err) + } + return &TestClient{client, mux, server}, nil +} + +func (c *TestClient) Close() { + c.server.Close() +} + +func (c *TestClient) AddHandler(pattern string, handler func(w http.ResponseWriter, r *http.Request)) { + c.mux.HandleFunc(pattern, handler) +} + +func GetTaskURLPath(uuid string) string { + return path.Join(baseURLPath, "tasks", uuid) +} From 379e3419a53aa97f0dec9ff50a83719f29586d90 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Wed, 3 Jan 2024 05:36:25 -0800 Subject: [PATCH 2/7] refactor: use wait.Poll function waiting for task state --- pkg/client/state.go | 86 +++++----------------------------------- pkg/client/state_test.go | 3 +- 2 files changed, 13 insertions(+), 76 deletions(-) diff --git a/pkg/client/state.go b/pkg/client/state.go index b3da3f9349..772a1a0e47 100644 --- a/pkg/client/state.go +++ b/pkg/client/state.go @@ -19,45 +19,26 @@ package client import ( "context" "fmt" - "math" "time" - ctrl "sigs.k8s.io/controller-runtime" - "github.com/nutanix-cloud-native/prism-go-client/utils" nutanixClientV3 "github.com/nutanix-cloud-native/prism-go-client/v3" + "k8s.io/apimachinery/pkg/util/wait" + ctrl "sigs.k8s.io/controller-runtime" ) -type stateRefreshFunc func() (string, error) +const ( + pollingInterval = time.Second * 2 + stateSucceeded = "SUCCEEDED" +) +// WaitForTaskCompletion will poll indefinitely every 2 seconds for the task with uuid to have status of "SUCCEEDED". +// Returns an error from GetTaskState or a timeout error if the context is cancelled. func WaitForTaskCompletion(ctx context.Context, conn *nutanixClientV3.Client, uuid string) error { - errCh := make(chan error, 1) - go waitForState( - errCh, - "SUCCEEDED", - waitUntilTaskStateFunc(ctx, conn, uuid)) - - err := <-errCh - return err -} - -func waitForState(errCh chan<- error, target string, refresh stateRefreshFunc) { - err := Retry(2, 2, 0, func(_ uint) (bool, error) { - state, err := refresh() - if err != nil { - return false, err - } else if state == target { - return true, nil - } - return false, nil + return wait.PollImmediateInfiniteWithContext(ctx, pollingInterval, func(ctx context.Context) (done bool, err error) { + state, getErr := GetTaskState(ctx, conn, uuid) + return state == stateSucceeded, getErr }) - errCh <- err -} - -func waitUntilTaskStateFunc(ctx context.Context, conn *nutanixClientV3.Client, uuid string) stateRefreshFunc { - return func() (string, error) { - return GetTaskState(ctx, conn, uuid) - } } func GetTaskState(ctx context.Context, client *nutanixClientV3.Client, taskUUID string) (string, error) { @@ -77,48 +58,3 @@ func GetTaskState(ctx context.Context, client *nutanixClientV3.Client, taskUUID log.V(1).Info(fmt.Sprintf("Status for task with UUID %s: %s", taskUUID, taskStatus)) return taskStatus, nil } - -// RetryableFunc performs an action and returns a bool indicating whether the -// function is done, or if it should keep retrying, and an error which will -// abort the retry and be returned by the Retry function. The 0-indexed attempt -// is passed with each call. -type RetryableFunc func(uint) (bool, error) - -/* -Retry retries a function up to numTries times with exponential backoff. -If numTries == 0, retry indefinitely. -If interval == 0, Retry will not delay retrying and there will be no -exponential backoff. -If maxInterval == 0, maxInterval is set to +Infinity. -Intervals are in seconds. -Returns an error if initial > max intervals, if retries are exhausted, or if the passed function returns -an error. -*/ -func Retry(initialInterval float64, maxInterval float64, numTries uint, function RetryableFunc) error { - if maxInterval == 0 { - maxInterval = math.Inf(1) - } else if initialInterval < 0 || initialInterval > maxInterval { - return fmt.Errorf("invalid retry intervals (negative or initial < max). Initial: %f, Max: %f", initialInterval, maxInterval) - } - - var err error - done := false - interval := initialInterval - for i := uint(0); !done && (numTries == 0 || i < numTries); i++ { - done, err = function(i) - if err != nil { - return err - } - - if !done { - // Retry after delay. Calculate next delay. - time.Sleep(time.Duration(interval) * time.Second) - interval = math.Min(interval*2, maxInterval) - } - } - - if !done { - return fmt.Errorf("function never succeeded in Retry") - } - return nil -} diff --git a/pkg/client/state_test.go b/pkg/client/state_test.go index a583bdb568..d01c2425b6 100644 --- a/pkg/client/state_test.go +++ b/pkg/client/state_test.go @@ -8,6 +8,7 @@ import ( "time" "github.com/stretchr/testify/assert" + "k8s.io/apimachinery/pkg/util/wait" nutanixTestClient "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/test/helpers/prism-go-client/v3" ) @@ -126,7 +127,7 @@ func Test_WaitForTaskCompletion(t *testing.T) { fmt.Fprint(w, `{"status": "PENDING"}`) }, ctx: ctx, - expectedErr: context.DeadlineExceeded, + expectedErr: wait.ErrWaitTimeout, }, } for _, tt := range tests { From 84edb0e02d03a08df1560a6a1c220a01b3641505 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Wed, 3 Jan 2024 05:46:42 -0800 Subject: [PATCH 3/7] refactor: use consistent task status names --- controllers/helpers.go | 2 +- controllers/nutanixmachine_controller.go | 2 +- pkg/client/{state.go => status.go} | 22 ++++++------ pkg/client/{state_test.go => status_test.go} | 36 ++++++++++---------- 4 files changed, 31 insertions(+), 31 deletions(-) rename pkg/client/{state.go => status.go} (64%) rename pkg/client/{state_test.go => status_test.go} (80%) diff --git a/controllers/helpers.go b/controllers/helpers.go index 8ec4331f22..70e6e2fca9 100644 --- a/controllers/helpers.go +++ b/controllers/helpers.go @@ -346,7 +346,7 @@ func GetImageUUID(ctx context.Context, client *nutanixClientV3.Client, imageName // HasTaskInProgress returns true if the given task is in progress func HasTaskInProgress(ctx context.Context, client *nutanixClientV3.Client, taskUUID string) (bool, error) { log := ctrl.LoggerFrom(ctx) - taskStatus, err := nutanixClientHelper.GetTaskState(ctx, client, taskUUID) + taskStatus, err := nutanixClientHelper.GetTaskStatus(ctx, client, taskUUID) if err != nil { return false, err } diff --git a/controllers/nutanixmachine_controller.go b/controllers/nutanixmachine_controller.go index 48ec100ceb..1b35b61204 100644 --- a/controllers/nutanixmachine_controller.go +++ b/controllers/nutanixmachine_controller.go @@ -666,7 +666,7 @@ func (r *NutanixMachineReconciler) getOrCreateVM(rctx *nctx.MachineContext) (*nu return nil, errorMsg } log.Info(fmt.Sprintf("Waiting for task %s to get completed for VM %s", lastTaskUUID, rctx.NutanixMachine.Name)) - err = nutanixClient.WaitForTaskCompletion(ctx, nc, lastTaskUUID) + err = nutanixClient.WaitForTaskToSucceed(ctx, nc, lastTaskUUID) if err != nil { errorMsg := fmt.Errorf("error occurred while waiting for task %s to start: %v", lastTaskUUID, err) rctx.SetFailureStatus(capierrors.CreateMachineError, errorMsg) diff --git a/pkg/client/state.go b/pkg/client/status.go similarity index 64% rename from pkg/client/state.go rename to pkg/client/status.go index 772a1a0e47..a56ca4069f 100644 --- a/pkg/client/state.go +++ b/pkg/client/status.go @@ -29,24 +29,24 @@ import ( const ( pollingInterval = time.Second * 2 - stateSucceeded = "SUCCEEDED" + statusSucceeded = "SUCCEEDED" ) -// WaitForTaskCompletion will poll indefinitely every 2 seconds for the task with uuid to have status of "SUCCEEDED". -// Returns an error from GetTaskState or a timeout error if the context is cancelled. -func WaitForTaskCompletion(ctx context.Context, conn *nutanixClientV3.Client, uuid string) error { +// WaitForTaskToSucceed will poll indefinitely every 2 seconds for the task with uuid to have status of "SUCCEEDED". +// Returns an error from GetTaskStatus or a timeout error if the context is cancelled. +func WaitForTaskToSucceed(ctx context.Context, conn *nutanixClientV3.Client, uuid string) error { return wait.PollImmediateInfiniteWithContext(ctx, pollingInterval, func(ctx context.Context) (done bool, err error) { - state, getErr := GetTaskState(ctx, conn, uuid) - return state == stateSucceeded, getErr + status, getErr := GetTaskStatus(ctx, conn, uuid) + return status == statusSucceeded, getErr }) } -func GetTaskState(ctx context.Context, client *nutanixClientV3.Client, taskUUID string) (string, error) { +func GetTaskStatus(ctx context.Context, client *nutanixClientV3.Client, uuid string) (string, error) { log := ctrl.LoggerFrom(ctx) - log.V(1).Info(fmt.Sprintf("Getting task with UUID %s", taskUUID)) - v, err := client.V3.GetTask(ctx, taskUUID) + log.V(1).Info(fmt.Sprintf("Getting task with UUID %s", uuid)) + v, err := client.V3.GetTask(ctx, uuid) if err != nil { - log.Error(err, fmt.Sprintf("error occurred while waiting for task with UUID %s", taskUUID)) + log.Error(err, fmt.Sprintf("error occurred while waiting for task with UUID %s", uuid)) return "", err } @@ -55,6 +55,6 @@ func GetTaskState(ctx context.Context, client *nutanixClientV3.Client, taskUUID fmt.Errorf("error_detail: %s, progress_message: %s", utils.StringValue(v.ErrorDetail), utils.StringValue(v.ProgressMessage)) } taskStatus := *v.Status - log.V(1).Info(fmt.Sprintf("Status for task with UUID %s: %s", taskUUID, taskStatus)) + log.V(1).Info(fmt.Sprintf("Status for task with UUID %s: %s", uuid, taskStatus)) return taskStatus, nil } diff --git a/pkg/client/state_test.go b/pkg/client/status_test.go similarity index 80% rename from pkg/client/state_test.go rename to pkg/client/status_test.go index d01c2425b6..99cdcbbc01 100644 --- a/pkg/client/state_test.go +++ b/pkg/client/status_test.go @@ -13,7 +13,7 @@ import ( nutanixTestClient "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/test/helpers/prism-go-client/v3" ) -func Test_GetTaskState(t *testing.T) { +func Test_GetTaskStatus(t *testing.T) { client, err := nutanixTestClient.NewTestClient() assert.NoError(t, err) // use cleanup over defer as the connection gets closed before the tests run with t.Parallel() @@ -23,12 +23,12 @@ func Test_GetTaskState(t *testing.T) { t.Parallel() tests := []struct { - name string - taskUUID string - handler func(w http.ResponseWriter, r *http.Request) - ctx context.Context - expectedState string - expectedErr error + name string + taskUUID string + handler func(w http.ResponseWriter, r *http.Request) + ctx context.Context + expectedStatus string + expectedErr error }{ { name: "succeeded", @@ -36,8 +36,8 @@ func Test_GetTaskState(t *testing.T) { handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, `{"status": "SUCCEEDED"}`) }, - ctx: context.Background(), - expectedState: "SUCCEEDED", + ctx: context.Background(), + expectedStatus: "SUCCEEDED", }, { name: "unauthorized", @@ -54,9 +54,9 @@ func Test_GetTaskState(t *testing.T) { handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, `{"status": "INVALID_UUID", "error_detail": "invalid UUID", "progress_message": "invalid UUID"}`) }, - ctx: context.Background(), - expectedState: "INVALID_UUID", - expectedErr: fmt.Errorf("error_detail: invalid UUID, progress_message: invalid UUID"), + ctx: context.Background(), + expectedStatus: "INVALID_UUID", + expectedErr: fmt.Errorf("error_detail: invalid UUID, progress_message: invalid UUID"), }, { name: "failed", @@ -64,9 +64,9 @@ func Test_GetTaskState(t *testing.T) { handler: func(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, `{"status": "FAILED", "error_detail": "task failed", "progress_message": "will never succeed"}`) }, - ctx: context.Background(), - expectedState: "FAILED", - expectedErr: fmt.Errorf("error_detail: task failed, progress_message: will never succeed"), + ctx: context.Background(), + expectedStatus: "FAILED", + expectedErr: fmt.Errorf("error_detail: task failed, progress_message: will never succeed"), }, } for _, tt := range tests { @@ -75,9 +75,9 @@ func Test_GetTaskState(t *testing.T) { t.Parallel() client.AddHandler(nutanixTestClient.GetTaskURLPath(tt.taskUUID), tt.handler) - state, err := GetTaskState(tt.ctx, client.Client, tt.taskUUID) + status, err := GetTaskStatus(tt.ctx, client.Client, tt.taskUUID) assert.Equal(t, tt.expectedErr, err) - assert.Equal(t, tt.expectedState, state) + assert.Equal(t, tt.expectedStatus, status) }) } } @@ -136,7 +136,7 @@ func Test_WaitForTaskCompletion(t *testing.T) { t.Parallel() client.AddHandler(nutanixTestClient.GetTaskURLPath(tt.taskUUID), tt.handler) - err := WaitForTaskCompletion(tt.ctx, client.Client, tt.taskUUID) + err := WaitForTaskToSucceed(tt.ctx, client.Client, tt.taskUUID) if tt.expectedErr != nil { assert.ErrorContains(t, err, tt.expectedErr.Error()) } else { From 59c2022b279b705dcf2edac6536be5be7acbd7c4 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Wed, 3 Jan 2024 05:48:56 -0800 Subject: [PATCH 4/7] fixup! test: add unit tests for pkg/client/state --- pkg/client/status_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/pkg/client/status_test.go b/pkg/client/status_test.go index 99cdcbbc01..63dc5c8491 100644 --- a/pkg/client/status_test.go +++ b/pkg/client/status_test.go @@ -10,11 +10,11 @@ import ( "github.com/stretchr/testify/assert" "k8s.io/apimachinery/pkg/util/wait" - nutanixTestClient "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/test/helpers/prism-go-client/v3" + nutanixtestclient "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/test/helpers/prism-go-client/v3" ) func Test_GetTaskStatus(t *testing.T) { - client, err := nutanixTestClient.NewTestClient() + client, err := nutanixtestclient.NewTestClient() assert.NoError(t, err) // use cleanup over defer as the connection gets closed before the tests run with t.Parallel() t.Cleanup(func() { @@ -73,7 +73,7 @@ func Test_GetTaskStatus(t *testing.T) { tt := tt // Capture range variable. t.Run(tt.name, func(t *testing.T) { t.Parallel() - client.AddHandler(nutanixTestClient.GetTaskURLPath(tt.taskUUID), tt.handler) + client.AddHandler(nutanixtestclient.GetTaskURLPath(tt.taskUUID), tt.handler) status, err := GetTaskStatus(tt.ctx, client.Client, tt.taskUUID) assert.Equal(t, tt.expectedErr, err) @@ -83,7 +83,7 @@ func Test_GetTaskStatus(t *testing.T) { } func Test_WaitForTaskCompletion(t *testing.T) { - client, err := nutanixTestClient.NewTestClient() + client, err := nutanixtestclient.NewTestClient() assert.NoError(t, err) // use cleanup over defer as the connection gets closed before the tests run with t.Parallel() t.Cleanup(func() { @@ -134,7 +134,7 @@ func Test_WaitForTaskCompletion(t *testing.T) { tt := tt // Capture range variable. t.Run(tt.name, func(t *testing.T) { t.Parallel() - client.AddHandler(nutanixTestClient.GetTaskURLPath(tt.taskUUID), tt.handler) + client.AddHandler(nutanixtestclient.GetTaskURLPath(tt.taskUUID), tt.handler) err := WaitForTaskToSucceed(tt.ctx, client.Client, tt.taskUUID) if tt.expectedErr != nil { From bb4a84c6310be30c96095e95874a1824cce0e617 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Thu, 4 Jan 2024 18:27:29 -0800 Subject: [PATCH 5/7] fix: revert to previous behaviod polling forever The ctx passed into WaitForTaskToSucceed is only used to cancel HTTP reqests and not to cancel the wait. --- pkg/client/status.go | 5 +++-- pkg/client/status_test.go | 11 +++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/pkg/client/status.go b/pkg/client/status.go index a56ca4069f..1bdc58e21c 100644 --- a/pkg/client/status.go +++ b/pkg/client/status.go @@ -33,9 +33,10 @@ const ( ) // WaitForTaskToSucceed will poll indefinitely every 2 seconds for the task with uuid to have status of "SUCCEEDED". -// Returns an error from GetTaskStatus or a timeout error if the context is cancelled. +// The polling will not stop if the ctx is cancelled, it's only used for HTTP requests in the client. +// WaitForTaskToSucceed will exit immediately on an error getting the task. func WaitForTaskToSucceed(ctx context.Context, conn *nutanixClientV3.Client, uuid string) error { - return wait.PollImmediateInfiniteWithContext(ctx, pollingInterval, func(ctx context.Context) (done bool, err error) { + return wait.PollImmediateInfinite(pollingInterval, func() (done bool, err error) { status, getErr := GetTaskStatus(ctx, conn, uuid) return status == statusSucceeded, getErr }) diff --git a/pkg/client/status_test.go b/pkg/client/status_test.go index 63dc5c8491..6e2a8a0aab 100644 --- a/pkg/client/status_test.go +++ b/pkg/client/status_test.go @@ -8,7 +8,6 @@ import ( "time" "github.com/stretchr/testify/assert" - "k8s.io/apimachinery/pkg/util/wait" nutanixtestclient "github.com/nutanix-cloud-native/cluster-api-provider-nutanix/test/helpers/prism-go-client/v3" ) @@ -90,7 +89,10 @@ func Test_WaitForTaskCompletion(t *testing.T) { client.Close() }) - ctx, cancel := context.WithTimeout(context.Background(), time.Second*1) + const ( + timeout = time.Second * 1 + ) + ctx, cancel := context.WithTimeout(context.Background(), timeout) t.Cleanup(func() { cancel() }) @@ -124,10 +126,11 @@ func Test_WaitForTaskCompletion(t *testing.T) { name: "timeout", taskUUID: "timeout", handler: func(w http.ResponseWriter, r *http.Request) { - fmt.Fprint(w, `{"status": "PENDING"}`) + // always wait 1 second longer than the timeout to force the context to cancel + time.Sleep(timeout + time.Second) }, ctx: ctx, - expectedErr: wait.ErrWaitTimeout, + expectedErr: context.DeadlineExceeded, }, } for _, tt := range tests { From 6a32d61cd445a09e2ce06a9effa00061e896e1d1 Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Thu, 4 Jan 2024 18:50:05 -0800 Subject: [PATCH 6/7] chore: add license headers --- pkg/client/status_test.go | 16 ++++++++++++++++ test/helpers/prism-go-client/v3/client.go | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/pkg/client/status_test.go b/pkg/client/status_test.go index 6e2a8a0aab..7427cd617c 100644 --- a/pkg/client/status_test.go +++ b/pkg/client/status_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2024 Nutanix + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package client import ( diff --git a/test/helpers/prism-go-client/v3/client.go b/test/helpers/prism-go-client/v3/client.go index 66ec96f112..4208b94815 100644 --- a/test/helpers/prism-go-client/v3/client.go +++ b/test/helpers/prism-go-client/v3/client.go @@ -1,3 +1,19 @@ +/* +Copyright 2024 Nutanix + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package v3 import ( From 946d1c2fb092cb6b49ee3ee82df6cbb3cdccb43e Mon Sep 17 00:00:00 2001 From: Dimitri Koshkin Date: Thu, 4 Jan 2024 19:38:25 -0800 Subject: [PATCH 7/7] fix: better function name --- pkg/client/status_test.go | 4 ++-- test/helpers/prism-go-client/v3/client.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/client/status_test.go b/pkg/client/status_test.go index 7427cd617c..d97da2d534 100644 --- a/pkg/client/status_test.go +++ b/pkg/client/status_test.go @@ -88,7 +88,7 @@ func Test_GetTaskStatus(t *testing.T) { tt := tt // Capture range variable. t.Run(tt.name, func(t *testing.T) { t.Parallel() - client.AddHandler(nutanixtestclient.GetTaskURLPath(tt.taskUUID), tt.handler) + client.AddMockHandler(nutanixtestclient.GetTaskURLPath(tt.taskUUID), tt.handler) status, err := GetTaskStatus(tt.ctx, client.Client, tt.taskUUID) assert.Equal(t, tt.expectedErr, err) @@ -153,7 +153,7 @@ func Test_WaitForTaskCompletion(t *testing.T) { tt := tt // Capture range variable. t.Run(tt.name, func(t *testing.T) { t.Parallel() - client.AddHandler(nutanixtestclient.GetTaskURLPath(tt.taskUUID), tt.handler) + client.AddMockHandler(nutanixtestclient.GetTaskURLPath(tt.taskUUID), tt.handler) err := WaitForTaskToSucceed(tt.ctx, client.Client, tt.taskUUID) if tt.expectedErr != nil { diff --git a/test/helpers/prism-go-client/v3/client.go b/test/helpers/prism-go-client/v3/client.go index 4208b94815..a904d7bba1 100644 --- a/test/helpers/prism-go-client/v3/client.go +++ b/test/helpers/prism-go-client/v3/client.go @@ -60,7 +60,7 @@ func (c *TestClient) Close() { c.server.Close() } -func (c *TestClient) AddHandler(pattern string, handler func(w http.ResponseWriter, r *http.Request)) { +func (c *TestClient) AddMockHandler(pattern string, handler func(w http.ResponseWriter, r *http.Request)) { c.mux.HandleFunc(pattern, handler) }