diff --git a/core/wait/wait.go b/core/wait/wait.go index 5c02e0027..55f7ff190 100644 --- a/core/wait/wait.go +++ b/core/wait/wait.go @@ -12,20 +12,25 @@ import ( var RetryHttpErrorStatusCodes = []int{http.StatusBadGateway, http.StatusGatewayTimeout} -type WaitFn func() (res interface{}, done bool, err error) - -type Handler struct { - fn WaitFn +// AsyncActionCheck reports whether a specific async action has finished. +// - waitFinished == true if the async action is finished, false otherwise. +// - response contains data regarding the current state of the resource targeted by the async action, if applicable. If not applicable, T should be struct{}. +// - err != nil if there was an error checking if the async action finished, or if it finished unsuccessfully. +type AsyncActionCheck[T any] func() (waitFinished bool, response *T, err error) + +// AsyncActionHandler handles waiting for a specific async action to be finished. +type AsyncActionHandler[T any] struct { + checkFn AsyncActionCheck[T] sleepBeforeWait time.Duration throttle time.Duration timeout time.Duration tempErrRetryLimit int } -// New creates a new Wait instance -func New(f WaitFn) *Handler { - return &Handler{ - fn: f, +// New initializes an AsyncActionHandler +func New[T any](f AsyncActionCheck[T]) *AsyncActionHandler[T] { + return &AsyncActionHandler[T]{ + checkFn: f, sleepBeforeWait: 0 * time.Second, throttle: 5 * time.Second, timeout: 30 * time.Minute, @@ -33,51 +38,51 @@ func New(f WaitFn) *Handler { } } -// SetThrottle sets the duration between func triggering -func (w *Handler) SetThrottle(d time.Duration) error { - if d == 0 { - return fmt.Errorf("throttle can't be 0") - } - w.throttle = d - return nil +// SetThrottle sets the time interval between each check of the async action. +func (h *AsyncActionHandler[T]) SetThrottle(d time.Duration) *AsyncActionHandler[T] { + h.throttle = d + return h } -// SetTimeout sets the duration for wait timeout -func (w *Handler) SetTimeout(d time.Duration) *Handler { - w.timeout = d - return w +// SetTimeout sets the duration for wait timeout. +func (h *AsyncActionHandler[T]) SetTimeout(d time.Duration) *AsyncActionHandler[T] { + h.timeout = d + return h } -// SetSleepBeforeWait sets the duration for sleep before wait -func (w *Handler) SetSleepBeforeWait(d time.Duration) *Handler { - w.sleepBeforeWait = d - return w +// SetSleepBeforeWait sets the duration for sleep before wait. +func (h *AsyncActionHandler[T]) SetSleepBeforeWait(d time.Duration) *AsyncActionHandler[T] { + h.sleepBeforeWait = d + return h } -// SetRetryLimitTempErr sets the retry limit if a temporary error is found. The list of temporary errors is defined in the RetryHttpErrorStatusCodes variable -func (w *Handler) SetRetryLimitTempErr(l int) *Handler { - w.tempErrRetryLimit = l - return w +// SetTempErrRetryLimit sets the retry limit if a temporary error is found. +// The list of temporary errors is defined in the RetryHttpErrorStatusCodes variable. +func (h *AsyncActionHandler[T]) SetTempErrRetryLimit(l int) *AsyncActionHandler[T] { + h.tempErrRetryLimit = l + return h } // WaitWithContext starts the wait until there's an error or wait is done -func (w *Handler) WaitWithContext(ctx context.Context) (res interface{}, err error) { - var done bool +func (h *AsyncActionHandler[T]) WaitWithContext(ctx context.Context) (res *T, err error) { + if h.throttle == 0 { + return nil, fmt.Errorf("throttle can't be 0") + } - ctx, cancel := context.WithTimeout(ctx, w.timeout) + ctx, cancel := context.WithTimeout(ctx, h.timeout) defer cancel() // Wait some seconds for the API to process the request - time.Sleep(w.sleepBeforeWait) + time.Sleep(h.sleepBeforeWait) - ticker := time.NewTicker(w.throttle) + ticker := time.NewTicker(h.throttle) defer ticker.Stop() var retryTempErrorCounter = 0 for { - res, done, err = w.fn() + done, res, err := h.checkFn() if err != nil { - retryTempErrorCounter, err = w.handleError(retryTempErrorCounter, err) + retryTempErrorCounter, err = h.handleError(retryTempErrorCounter, err) if err != nil { return res, err } @@ -95,18 +100,18 @@ func (w *Handler) WaitWithContext(ctx context.Context) (res interface{}, err err } } -func (w *Handler) handleError(retryTempErrorCounter int, err error) (int, error) { +func (h *AsyncActionHandler[T]) handleError(retryTempErrorCounter int, err error) (int, error) { 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 if !ok { - return retryTempErrorCounter, fmt.Errorf("could not convert error to GenericOpenApiError, %w", err) + return retryTempErrorCounter, fmt.Errorf("found non-GenericOpenApiError: %w", err) } // Some APIs may return temporary errors and the request should be retried - if utils.Contains(RetryHttpErrorStatusCodes, oapiErr.StatusCode) { - retryTempErrorCounter++ - if retryTempErrorCounter == w.tempErrRetryLimit { - return retryTempErrorCounter, fmt.Errorf("temporary error was found and the retry limit was reached: %w", err) - } - return retryTempErrorCounter, nil + if !utils.Contains(RetryHttpErrorStatusCodes, oapiErr.StatusCode) { + return retryTempErrorCounter, err + } + retryTempErrorCounter++ + if retryTempErrorCounter == h.tempErrRetryLimit { + return retryTempErrorCounter, fmt.Errorf("temporary error was found and the retry limit was reached: %w", err) } - return retryTempErrorCounter, fmt.Errorf("executing wait function: %w", err) + return retryTempErrorCounter, nil } diff --git a/core/wait/wait_test.go b/core/wait/wait_test.go index 98c0f4d31..e7d1ba7a2 100644 --- a/core/wait/wait_test.go +++ b/core/wait/wait_test.go @@ -8,186 +8,382 @@ import ( "time" "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" "github.com/stackitcloud/stackit-sdk-go/core/oapierror" ) +// Options used for comparing AsyncActionHandler +var cmpOpts = []cmp.Option{ + cmp.AllowUnexported(AsyncActionHandler[interface{}]{}), + cmpopts.IgnoreFields(AsyncActionHandler[interface{}]{}, "checkFn"), // cmp won't compare functions well +} + func TestNew(t *testing.T) { - simple := func() (res interface{}, done bool, err error) { return nil, true, nil } - type args struct { - f WaitFn + checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil } + got := New(checkFn) + want := &AsyncActionHandler[interface{}]{ + checkFn: checkFn, + sleepBeforeWait: 0 * time.Second, + throttle: 5 * time.Second, + timeout: 30 * time.Minute, + tempErrRetryLimit: 5, } - tests := []struct { - name string - args args - want *Handler - }{ - {"ok", args{simple}, &Handler{fn: simple, throttle: 5 * time.Second, tempErrRetryLimit: 10}}, + + diff := cmp.Diff(got, want, cmpOpts...) + if diff != "" { + t.Errorf("Data does not match: %s", diff) } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := New(tt.args.f); !cmp.Equal(got.throttle, tt.want.throttle) { - t.Errorf("New() = %v, want %v", got, tt.want) +} + +func TestSetThrottle(t *testing.T) { + checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil } + + for _, tt := range []struct { + desc string + throttle time.Duration + }{ + { + "base_1", + time.Hour, + }, + { + "base_2", + 10 * time.Millisecond, + }, + { + "base_3", + 0, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + want := New(checkFn) + want.throttle = tt.throttle + got := New(checkFn) + got.SetThrottle(tt.throttle) + + diff := cmp.Diff(got, want, cmpOpts...) + if diff != "" { + t.Errorf("Data does not match: %s", diff) } }) } } -func TestSetThrottle(t *testing.T) { - simple := func() (res interface{}, done bool, err error) { return nil, true, nil } - type args struct { - d time.Duration - } - tests := []struct { - name string - args args - want error +func TestSetTimeout(t *testing.T) { + checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil } + + for _, tt := range []struct { + desc string + timeout time.Duration }{ - {"ok", args{10 * time.Second}, nil}, - {"err", args{0 * time.Second}, fmt.Errorf("throttle can't be 0")}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := New(simple) - got := w.SetThrottle(tt.args.d) - if got == nil && tt.want != nil { - t.Errorf("Wait.SetThrottle() = %v, want %v", got, tt.want) - } - if got != nil && tt.want != nil { - if got.Error() != tt.want.Error() { - t.Errorf("Wait.SetThrottle() = %v, want %v", got, tt.want) - } + { + "base_1", + time.Hour, + }, + { + "base_2", + 10 * time.Millisecond, + }, + { + "base_3", + 0, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + want := New(checkFn) + want.timeout = tt.timeout + got := New(checkFn) + got.SetTimeout(tt.timeout) + + diff := cmp.Diff(got, want, cmpOpts...) + if diff != "" { + t.Errorf("Data does not match: %s", diff) } }) } } func TestSetSleepBeforeWait(t *testing.T) { - f := &Handler{ - sleepBeforeWait: 1 * time.Minute, - } + checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil } - type fields struct { + for _, tt := range []struct { + desc string sleepBeforeWait time.Duration - } - type args struct { - d time.Duration - } - tests := []struct { - name string - fields fields - args args - want *Handler }{ - {"ok", fields{sleepBeforeWait: 30 * time.Second}, args{d: 1 * time.Minute}, f}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &Handler{ - sleepBeforeWait: tt.fields.sleepBeforeWait, - } - if got := w.SetSleepBeforeWait(tt.args.d); !cmp.Equal(got, tt.want, cmp.AllowUnexported(Handler{})) { - t.Errorf("Wait.SetSleepBeforeWait() = %v, want %v", got, tt.want) + { + "base_1", + time.Hour, + }, + { + "base_2", + 10 * time.Millisecond, + }, + { + "base_3", + 0, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + want := New(checkFn) + want.sleepBeforeWait = tt.sleepBeforeWait + got := New(checkFn) + got.SetSleepBeforeWait(tt.sleepBeforeWait) + + diff := cmp.Diff(got, want, cmpOpts...) + if diff != "" { + t.Errorf("Data does not match: %s", diff) } }) } } -func TestWaitWithContext(t *testing.T) { - ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) - defer cancel() +func TestSetTempErrRetryLimit(t *testing.T) { + checkFn := func() (waitFinished bool, res *interface{}, err error) { return true, nil, nil } - type fields struct { - fn WaitFn - throttle time.Duration - timeout time.Duration + for _, tt := range []struct { + desc string tempErrRetryLimit int - } - tests := []struct { - name string - fields fields - wantDone bool - wantErr bool }{ - {"ok", fields{throttle: 1 * time.Second, timeout: 1 * time.Hour, fn: func() (res interface{}, done bool, err error) { - return nil, true, nil - }}, true, false}, + { + "base_1", + 2, + }, + { + "base_3", + 0, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + want := New(checkFn) + want.tempErrRetryLimit = tt.tempErrRetryLimit + got := New(checkFn) + got.SetTempErrRetryLimit(tt.tempErrRetryLimit) - {"ok 2", fields{throttle: 200 * time.Millisecond, timeout: 1 * time.Hour, tempErrRetryLimit: 5, fn: func() (res interface{}, done bool, err error) { - if ctx.Err() == nil { - return nil, false, nil + diff := cmp.Diff(got, want, cmpOpts...) + if diff != "" { + t.Errorf("Data does not match: %s", diff) } - return nil, true, nil - }}, true, false}, + }) + } +} - {"err", fields{throttle: 1 * time.Millisecond, timeout: 1 * time.Hour, fn: func() (res interface{}, done bool, err error) { - return nil, true, fmt.Errorf("something happened") - }}, true, true}, +func TestWaitWithContext(t *testing.T) { + for _, tt := range []struct { + desc string + checkFnNumberCallsToFinishWait int + checkFnWaitSucceeds bool + checkFnNumberCallsUntilErr int + checkFnReturnsTempErr bool + handlerSleepBeforeWait time.Duration + handlerThrottle time.Duration + handlerTimeout time.Duration + handlerTempErrRetryLimit int + contextTimeout time.Duration + wantCheckFnNumberCalls int + wantErr bool + }{ + { + desc: "base", + checkFnNumberCallsToFinishWait: 1, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 100 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 100 * time.Millisecond, + wantCheckFnNumberCalls: 1, + wantErr: false, + }, + { + desc: "bad_trottle", + checkFnNumberCallsToFinishWait: 1, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 0, + handlerThrottle: 0 * time.Millisecond, + handlerTimeout: 100 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 100 * time.Millisecond, + wantCheckFnNumberCalls: 0, + wantErr: true, + }, + { + desc: "throttle", + checkFnNumberCallsToFinishWait: 3, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 100 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 100 * time.Millisecond, + wantCheckFnNumberCalls: 3, + wantErr: false, + }, + { + desc: "throttle_timeout_1", + checkFnNumberCallsToFinishWait: 999999, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 100 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 1000 * time.Millisecond, + wantCheckFnNumberCalls: 4, + wantErr: true, + }, + { + desc: "throttle_timeout_2", + checkFnNumberCallsToFinishWait: 999999, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 1000 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 100 * time.Millisecond, + wantCheckFnNumberCalls: 4, + wantErr: true, + }, + { + desc: "set_sleep_before_wait_and_throttle", + checkFnNumberCallsToFinishWait: 999999, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 60 * time.Millisecond, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 100 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 100 * time.Millisecond, + wantCheckFnNumberCalls: 2, + wantErr: true, + }, + { + desc: "set_sleep_before_wait_timeout_1", + checkFnNumberCallsToFinishWait: 2, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 200 * time.Millisecond, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 100 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 1000 * time.Millisecond, + wantCheckFnNumberCalls: 1, + wantErr: true, + }, + { + desc: "set_sleep_before_wait_timeout_2", + checkFnNumberCallsToFinishWait: 2, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 999999, + handlerSleepBeforeWait: 200 * time.Millisecond, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 1000 * time.Millisecond, + handlerTempErrRetryLimit: 0, + contextTimeout: 100 * time.Millisecond, + wantCheckFnNumberCalls: 1, + wantErr: true, + }, + { + desc: "retry_limit_temp_err_1", + checkFnNumberCallsToFinishWait: 999999, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 0, + checkFnReturnsTempErr: false, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 1000 * time.Millisecond, + handlerTempErrRetryLimit: 5, + contextTimeout: 1000 * time.Millisecond, + wantCheckFnNumberCalls: 1, + wantErr: true, + }, + { + desc: "retry_limit_temp_err_2", + checkFnNumberCallsToFinishWait: 999999, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 1, + checkFnReturnsTempErr: true, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 1000 * time.Millisecond, + handlerTempErrRetryLimit: 5, + contextTimeout: 1000 * time.Millisecond, + wantCheckFnNumberCalls: 5, + wantErr: true, + }, + { + desc: "retry_limit_temp_err_3", + checkFnNumberCallsToFinishWait: 3, + checkFnWaitSucceeds: true, + checkFnNumberCallsUntilErr: 1, + checkFnReturnsTempErr: true, + handlerSleepBeforeWait: 0, + handlerThrottle: 30 * time.Millisecond, + handlerTimeout: 1000 * time.Millisecond, + handlerTempErrRetryLimit: 5, + contextTimeout: 1000 * time.Millisecond, + wantCheckFnNumberCalls: 3, + wantErr: false, + }, + } { + t.Run(tt.desc, func(t *testing.T) { + type respType struct{} - {"err 2", fields{throttle: 1 * time.Millisecond, timeout: 1 * time.Hour, fn: func() (res interface{}, done bool, err error) { - return nil, false, fmt.Errorf("something happened") - }}, true, true}, + numberCheckFnCalls := 0 + checkFn := func() (waitFinished bool, response *respType, err error) { + numberCheckFnCalls++ + if numberCheckFnCalls == tt.checkFnNumberCallsToFinishWait { + if tt.checkFnWaitSucceeds { + return true, &respType{}, nil + } + return true, &respType{}, fmt.Errorf("the async action couldn't be done") + } - {"timeout", fields{throttle: 1 * time.Millisecond, timeout: 1 * time.Millisecond, fn: func() (res interface{}, done bool, err error) { - return nil, false, nil - }}, false, true}, + if numberCheckFnCalls < tt.checkFnNumberCallsUntilErr { + return false, nil, nil + } - {"tempErrorLimitReached", fields{throttle: 1 * time.Millisecond, timeout: 1 * time.Millisecond, fn: func() (res interface{}, done bool, err error) { - return nil, false, nil - }}, false, true}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &Handler{ - fn: tt.fields.fn, - throttle: tt.fields.throttle, - timeout: tt.fields.timeout, - tempErrRetryLimit: tt.fields.tempErrRetryLimit, + if tt.checkFnReturnsTempErr { + return false, nil, &oapierror.GenericOpenAPIError{ + StatusCode: RetryHttpErrorStatusCodes[0], + ErrorMessage: "something bad happened when checking if the async action was finished", + } + } + return false, nil, fmt.Errorf("something bad happened when checking if the async action was finished") } - _, err := w.WaitWithContext(context.Background()) - if (err != nil) != tt.wantErr { - t.Errorf("Wait.Run() error = %v, wantErr %v", err, tt.wantErr) - return + handler := AsyncActionHandler[respType]{ + checkFn: checkFn, + sleepBeforeWait: tt.handlerSleepBeforeWait, + throttle: tt.handlerThrottle, + timeout: tt.handlerTimeout, + tempErrRetryLimit: tt.handlerTempErrRetryLimit, } - }) - } -} + ctx, cancel := context.WithTimeout(context.Background(), tt.contextTimeout) + defer cancel() -func TestSetTimeout(t *testing.T) { - f := &Handler{ - throttle: 5 * time.Second, - timeout: 5 * time.Hour, - } + resp, err := handler.WaitWithContext(ctx) - type fields struct { - throttle time.Duration - timeout time.Duration - } - type args struct { - d time.Duration - } - tests := []struct { - name string - fields fields - args args - want *Handler - }{ - {"ok", fields{timeout: 1 * time.Hour, throttle: 5 * time.Second}, args{d: 5 * time.Hour}, f}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - w := &Handler{ - throttle: tt.fields.throttle, - timeout: tt.fields.timeout, + if tt.wantErr && (err == nil) { + t.Errorf("expected error but got none") } - if got := w.SetTimeout(tt.args.d); !cmp.Equal(got, tt.want, cmp.AllowUnexported(Handler{})) { - t.Errorf("Wait.SetTimeout() = %v, want %v", got, tt.want) + if !tt.wantErr && (err != nil) { + t.Errorf("expected no error but got \"%v\"", err) + } + if (err == nil) && (resp == nil) { + t.Errorf("got nil err but nil resp") + } + if numberCheckFnCalls != tt.wantCheckFnNumberCalls { + t.Errorf("expected %d calls to checkFn but got %d instead", tt.wantCheckFnNumberCalls, numberCheckFnCalls) } }) } } func TestHandleError(t *testing.T) { - tests := []struct { + for _, tt := range []struct { desc string reqErr error tempErrRetryLimit int @@ -231,10 +427,9 @@ func TestHandleError(t *testing.T) { tempErrRetryLimit: 1, wantErr: true, }, - } - for _, tt := range tests { + } { t.Run(tt.desc, func(t *testing.T) { - w := &Handler{ + w := &AsyncActionHandler[interface{}]{ tempErrRetryLimit: tt.tempErrRetryLimit, } _, err := w.handleError(0, tt.reqErr) diff --git a/examples/waiter/waiter.go b/examples/waiter/waiter.go index 70eda809b..71c008d61 100644 --- a/examples/waiter/waiter.go +++ b/examples/waiter/waiter.go @@ -38,20 +38,13 @@ func main() { } zoneId := *createZoneResp.Zone.Id + + // The following will wait until the DNS zone creation has finished wres, err := wait.CreateZoneWaitHandler(ctx, dnsClient, projectId, zoneId).SetTimeout(15 * time.Minute).WaitWithContext(ctx) if err != nil { - fmt.Fprintf(os.Stderr, "[DNS API] Waiting for zone update: %v\n", err) - os.Exit(1) - } - - // At this stage the waiter is waiting for an update to the zone - // You can make a manual request to the DNS API updating the zone that was just created - // The waiter will finish, and you will get the output below - got, ok := wres.(*dns.ZoneResponse) - if !ok { - fmt.Fprintf(os.Stderr, "[DNS API] Returned response has unexpected type: %v\n", err) + fmt.Fprintf(os.Stderr, "[DNS API] Waiting for zone creation: %v\n", err) os.Exit(1) } - fmt.Fprintf(os.Stderr, "[DNS API] Zone with id %s update (state: %s)\n", *got.Zone.Id, *got.Zone.State) + fmt.Fprintf(os.Stderr, "[DNS API] Zone with id %s created (state: %s)\n", *wres.Zone.Id, *wres.Zone.State) // The state is always successful } diff --git a/services/argus/wait/wait.go b/services/argus/wait/wait.go index 164b57c1f..db04aa173 100644 --- a/services/argus/wait/wait.go +++ b/services/argus/wait/wait.go @@ -23,99 +23,97 @@ type APIClientInterface interface { GetScrapeConfigsExecute(ctx context.Context, instanceId, projectId string) (*argus.ScrapeConfigsResponse, error) } -// will wait for creation -// -// returned interface is nil or *InstanceResponse -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInterface, instanceId, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInterface, instanceId, projectId string) *wait.AsyncActionHandler[argus.InstanceResponse] { + return wait.New(func() (waitFinished bool, response *argus.InstanceResponse, err error) { s, err := a.GetInstanceExecute(ctx, instanceId, projectId) if err != nil { - return nil, false, err + return false, nil, err } if s.Id == nil || s.Status == nil { - return s, false, fmt.Errorf("could not get instance id or status from response for project %s and instance %s", projectId, instanceId) + return false, nil, fmt.Errorf("could not get instance id or status from response for project %s and instance %s", projectId, instanceId) } if *s.Id == instanceId && *s.Status == CreateSuccess { - return s, true, nil + return true, s, nil } if *s.Id == instanceId && *s.Status == CreateFail { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -// returned interface is nil or *InstanceResponse -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInterface, instanceId, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInterface, instanceId, projectId string) *wait.AsyncActionHandler[argus.InstanceResponse] { + return wait.New(func() (waitFinished bool, response *argus.InstanceResponse, err error) { s, err := a.GetInstanceExecute(ctx, instanceId, projectId) if err != nil { - return nil, false, err + return false, nil, err } if s.Id == nil || s.Status == nil { - return s, false, fmt.Errorf("could not get instance id or status from response for project %s and instance %s", projectId, instanceId) + return false, nil, fmt.Errorf("could not get instance id or status from response for project %s and instance %s", projectId, instanceId) } // The argus instance API currently replies with create success in case the update was successful. if *s.Id == instanceId && (*s.Status == UpdateSuccess || *s.Status == CreateSuccess) { - return s, true, nil + return true, s, nil } if *s.Id == instanceId && (*s.Status == UpdateFail || *s.Status == CreateFail) { - return s, true, fmt.Errorf("update failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -// returned interface is nil or *InstanceResponse -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInterface, instanceId, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInterface, instanceId, projectId string) *wait.AsyncActionHandler[argus.InstanceResponse] { + return wait.New(func() (waitFinished bool, response *argus.InstanceResponse, err error) { s, err := a.GetInstanceExecute(ctx, instanceId, projectId) if err != nil { - return nil, false, err + return false, nil, err } if s.Id == nil || s.Status == nil { - return s, false, fmt.Errorf("could not get instance id or status from response for project %s and instance %s", projectId, instanceId) + return false, nil, fmt.Errorf("could not get instance id or status from response for project %s and instance %s", projectId, instanceId) } if *s.Id == instanceId && *s.Status == DeleteSuccess { - return s, true, nil + return true, s, nil } if *s.Id == instanceId && *s.Status == DeleteFail { - return s, true, fmt.Errorf("delete failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("delete failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -func CreateScrapeConfigWaitHandler(ctx context.Context, a APIClientInterface, instanceId, jobName, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateScrapeConfigWaitHandler will wait for scrape config creation +func CreateScrapeConfigWaitHandler(ctx context.Context, a APIClientInterface, instanceId, jobName, projectId string) *wait.AsyncActionHandler[argus.ScrapeConfigsResponse] { + return wait.New(func() (waitFinished bool, response *argus.ScrapeConfigsResponse, err error) { s, err := a.GetScrapeConfigsExecute(ctx, instanceId, projectId) if err != nil { - return nil, false, err + return false, nil, err } jobs := *s.Data for i := range jobs { if *jobs[i].JobName == jobName { - return s, true, nil + return true, s, nil } } - return s, false, nil + return false, nil, nil }) } -func DeleteScrapeConfigWaitHandler(ctx context.Context, a APIClientInterface, instanceId, jobName, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteScrapeConfigWaitHandler will wait for scrape config deletion +func DeleteScrapeConfigWaitHandler(ctx context.Context, a APIClientInterface, instanceId, jobName, projectId string) *wait.AsyncActionHandler[argus.ScrapeConfigsResponse] { + return wait.New(func() (waitFinished bool, response *argus.ScrapeConfigsResponse, err error) { s, err := a.GetScrapeConfigsExecute(ctx, instanceId, projectId) if err != nil { - return nil, false, err + return false, nil, err } jobs := *s.Data for i := range jobs { if *jobs[i].JobName == jobName { - return s, false, nil + return false, nil, nil } } - return s, true, nil + return true, s, nil }) } diff --git a/services/argus/wait/wait_test.go b/services/argus/wait/wait_test.go index ee73b4376..42598fe64 100644 --- a/services/argus/wait/wait_test.go +++ b/services/argus/wait/wait_test.go @@ -49,36 +49,42 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState *string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: utils.Ptr(CreateSuccess), wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: utils.Ptr(CreateFail), wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: utils.Ptr(""), wantErr: true, + wantResp: false, }, { desc: "broken_response", getFails: false, resourceState: nil, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: utils.Ptr("ANOTHER STATE"), wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -89,13 +95,11 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *argus.InstanceResponse - if !tt.getFails { + if tt.wantResp { wantRes = &argus.InstanceResponse{ Id: utils.Ptr("iid"), Status: tt.resourceState, } - } else { - wantRes = nil } handler := CreateInstanceWaitHandler(context.Background(), apiClient, "iid", "pid") @@ -105,10 +109,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { + if !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -121,30 +122,35 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState *string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: utils.Ptr(UpdateSuccess), wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: utils.Ptr(UpdateFail), wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: utils.Ptr(""), wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: utils.Ptr("ANOTHER STATE"), wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -155,13 +161,11 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *argus.InstanceResponse - if !tt.getFails { + if tt.wantResp { wantRes = &argus.InstanceResponse{ Status: tt.resourceState, Id: utils.Ptr("iid"), } - } else { - wantRes = nil } handler := UpdateInstanceWaitHandler(context.Background(), apiClient, "iid", "pid") @@ -171,10 +175,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { + if !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -187,30 +188,35 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { getFails bool resourceState *string wantErr bool + wantResp bool }{ { desc: "delete_succeeded", getFails: false, resourceState: utils.Ptr(DeleteSuccess), wantErr: false, + wantResp: true, }, { desc: "delete_failed", getFails: false, resourceState: utils.Ptr(DeleteFail), wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: utils.Ptr(""), wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: utils.Ptr("ANOTHER STATE"), wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -221,13 +227,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { } var wantRes *argus.InstanceResponse - if !tt.getFails { + if tt.wantResp { wantRes = &argus.InstanceResponse{ Status: tt.resourceState, Id: utils.Ptr("iid"), } - } else { - wantRes = nil } handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "iid", "pid") @@ -237,10 +241,7 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { + if !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -253,24 +254,28 @@ func TestCreateScrapeConfigWaitHandler(t *testing.T) { getFails bool jobs []argus.Job wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, jobs: []argus.Job{{JobName: utils.Ptr("job")}, {JobName: utils.Ptr("other-job")}}, wantErr: false, + wantResp: true, }, { desc: "create_failed and timeout", getFails: false, jobs: []argus.Job{{JobName: utils.Ptr("other-job")}}, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, jobs: []argus.Job{}, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -281,12 +286,10 @@ func TestCreateScrapeConfigWaitHandler(t *testing.T) { } var wantRes *argus.ScrapeConfigsResponse - if !tt.getFails { + if tt.wantResp { wantRes = &argus.ScrapeConfigsResponse{ Data: &tt.jobs, } - } else { - wantRes = nil } handler := CreateScrapeConfigWaitHandler(context.Background(), apiClient, "", "job", "") @@ -296,10 +299,7 @@ func TestCreateScrapeConfigWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { + if !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -312,24 +312,28 @@ func TestDeleteScrapeConfigWaitHandler(t *testing.T) { getFails bool jobs []argus.Job wantErr bool + wantResp bool }{ { desc: "delete_succeeded", getFails: false, jobs: []argus.Job{{JobName: utils.Ptr("other-job")}}, wantErr: false, + wantResp: true, }, { desc: "timeout", getFails: false, jobs: []argus.Job{{JobName: utils.Ptr("job")}}, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, jobs: []argus.Job{}, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -340,12 +344,10 @@ func TestDeleteScrapeConfigWaitHandler(t *testing.T) { } var wantRes *argus.ScrapeConfigsResponse - if !tt.getFails { + if tt.wantResp { wantRes = &argus.ScrapeConfigsResponse{ Data: &tt.jobs, } - } else { - wantRes = nil } handler := DeleteScrapeConfigWaitHandler(context.Background(), apiClient, "", "job", "") @@ -355,10 +357,7 @@ func TestDeleteScrapeConfigWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { + if !cmp.Equal(gotRes, wantRes, cmpopts.IgnoreUnexported(argus.NullableString{})) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) diff --git a/services/dns/wait/wait.go b/services/dns/wait/wait.go index bcefb1aba..e223845c0 100644 --- a/services/dns/wait/wait.go +++ b/services/dns/wait/wait.go @@ -23,128 +23,124 @@ type APIClientInterface interface { GetRecordSetExecute(ctx context.Context, projectId, zoneId, rrSetId string) (*dns.RecordSetResponse, error) } -// CreateZoneWaitHandler will wait for creation -// returned interface is nil or *ZoneResponseZone -func CreateZoneWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateZoneWaitHandler will wait for zone creation +func CreateZoneWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[dns.ZoneResponse] { + return wait.New(func() (waitFinished bool, response *dns.ZoneResponse, err error) { s, err := a.GetZoneExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.Zone.Id == nil || s.Zone.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the id or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the id or the state are missing", instanceId) } if *s.Zone.Id == instanceId && *s.Zone.State == CreateSuccess { - return s, true, nil + return true, s, nil } if *s.Zone.Id == instanceId && *s.Zone.State == CreateFail { - return s, true, fmt.Errorf("create failed for zone with id %s", instanceId) + return true, s, fmt.Errorf("create failed for zone with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateZoneWaitHandler will wait for update -// returned interface is nil or *ZoneResponseZone -func UpdateZoneWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateZoneWaitHandler will wait for zone update +func UpdateZoneWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[dns.ZoneResponse] { + return wait.New(func() (waitFinished bool, response *dns.ZoneResponse, err error) { s, err := a.GetZoneExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.Zone.Id == nil || s.Zone.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the id or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the id or the state are missing", instanceId) } if *s.Zone.Id == instanceId && *s.Zone.State == UpdateSuccess { - return s, true, nil + return true, s, nil } if *s.Zone.Id == instanceId && *s.Zone.State == UpdateFail { - return s, true, fmt.Errorf("update failed for zone with id %s", instanceId) + return true, s, fmt.Errorf("update failed for zone with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteZoneWaitHandler will wait for delete +// DeleteZoneWaitHandler will wait for zone deletion // returned interface is nil or *ZoneResponseZone -func DeleteZoneWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +func DeleteZoneWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[dns.ZoneResponse] { + return wait.New(func() (waitFinished bool, response *dns.ZoneResponse, err error) { s, err := a.GetZoneExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.Zone.Id == nil || s.Zone.State == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: the id or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: the id or the state are missing", instanceId) } if *s.Zone.Id == instanceId && *s.Zone.State == DeleteSuccess { - return s, true, nil + return true, s, nil } if *s.Zone.Id == instanceId && *s.Zone.State == DeleteFail { - return s, true, fmt.Errorf("delete failed for zone with id %s", instanceId) + return true, s, fmt.Errorf("delete failed for zone with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// CreateRecordWaitHandler will wait for creation -// returned interface is nil or *RecordSetResponse -func CreateRecordSetWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, rrSetId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateRecordWaitHandler will wait for recordset creation +func CreateRecordSetWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, rrSetId string) *wait.AsyncActionHandler[dns.RecordSetResponse] { + return wait.New(func() (waitFinished bool, response *dns.RecordSetResponse, err error) { s, err := a.GetRecordSetExecute(ctx, projectId, instanceId, rrSetId) if err != nil { - return nil, false, err + return false, nil, err } if s.Rrset.Id == nil || s.Rrset.State == nil { - return s, false, fmt.Errorf("create failed for record set with id %s. The response is not valid: the id or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for record set with id %s. The response is not valid: the id or the state are missing", instanceId) } if *s.Rrset.Id == rrSetId && *s.Rrset.State == CreateSuccess { - return s, true, nil + return true, s, nil } if *s.Rrset.Id == rrSetId && *s.Rrset.State == CreateFail { - return s, true, fmt.Errorf("create failed for record with id %s", rrSetId) + return true, s, fmt.Errorf("create failed for record with id %s", rrSetId) } - return s, false, nil + return false, nil, nil }) } -// UpdateRecordWaitHandler will wait for update -// returned interface is nil or *RecordSetResponse -func UpdateRecordSetWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, rrSetId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateRecordWaitHandler will wait for recordset update +func UpdateRecordSetWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, rrSetId string) *wait.AsyncActionHandler[dns.RecordSetResponse] { + return wait.New(func() (waitFinished bool, response *dns.RecordSetResponse, err error) { s, err := a.GetRecordSetExecute(ctx, projectId, instanceId, rrSetId) if err != nil { - return nil, false, err + return false, nil, err } if s.Rrset.Id == nil || s.Rrset.State == nil { - return s, false, fmt.Errorf("update failed for record set with id %s. The response is not valid: the id or the state are missing", rrSetId) + return false, nil, fmt.Errorf("update failed for record set with id %s. The response is not valid: the id or the state are missing", rrSetId) } if *s.Rrset.Id == rrSetId && *s.Rrset.State == UpdateSuccess { - return s, true, nil + return true, s, nil } if *s.Rrset.Id == rrSetId && *s.Rrset.State == UpdateFail { - return s, true, fmt.Errorf("update failed for record with id %s", rrSetId) + return true, s, fmt.Errorf("update failed for record with id %s", rrSetId) } - return s, false, nil + return false, nil, nil }) } // DeleteRecordWaitHandler will wait for deletion // returned interface is nil or *RecordSetResponse -func DeleteRecordSetWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, rrSetId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +func DeleteRecordSetWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId, rrSetId string) *wait.AsyncActionHandler[dns.RecordSetResponse] { + return wait.New(func() (waitFinished bool, response *dns.RecordSetResponse, err error) { s, err := a.GetRecordSetExecute(ctx, projectId, instanceId, rrSetId) if err != nil { - return nil, false, err + return false, nil, err } if s.Rrset.Id == nil || s.Rrset.State == nil { - return s, false, fmt.Errorf("delete failed for record set with id %s. The response is not valid: the id or the state are missing", rrSetId) + return false, nil, fmt.Errorf("delete failed for record set with id %s. The response is not valid: the id or the state are missing", rrSetId) } if *s.Rrset.Id == rrSetId && *s.Rrset.State == DeleteSuccess { - return s, true, nil + return true, s, nil } if *s.Rrset.Id == rrSetId && *s.Rrset.State == DeleteFail { - return s, true, fmt.Errorf("delete failed for record with id %s", rrSetId) + return true, s, fmt.Errorf("delete failed for record with id %s", rrSetId) } - return s, false, nil + return false, nil, nil }) } diff --git a/services/dns/wait/wait_test.go b/services/dns/wait/wait_test.go index 9d7329eda..9a8fa52ca 100644 --- a/services/dns/wait/wait_test.go +++ b/services/dns/wait/wait_test.go @@ -52,30 +52,35 @@ func TestCreateZoneWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: CreateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: CreateFail, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: "", wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -86,15 +91,13 @@ func TestCreateZoneWaitHandler(t *testing.T) { } var wantRes *dns.ZoneResponse - if !tt.getFails { + if tt.wantResp { wantRes = &dns.ZoneResponse{ Zone: &dns.Zone{ State: &tt.resourceState, Id: utils.Ptr("zid"), }, } - } else { - wantRes = nil } handler := CreateZoneWaitHandler(context.Background(), apiClient, "pid", "zid") @@ -104,10 +107,7 @@ func TestCreateZoneWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -120,30 +120,35 @@ func TestUpdateZoneWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: UpdateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: UpdateFail, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: "", wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -154,15 +159,13 @@ func TestUpdateZoneWaitHandler(t *testing.T) { } var wantRes *dns.ZoneResponse - if !tt.getFails { + if tt.wantResp { wantRes = &dns.ZoneResponse{ Zone: &dns.Zone{ State: &tt.resourceState, Id: utils.Ptr("zid"), }, } - } else { - wantRes = nil } handler := UpdateZoneWaitHandler(context.Background(), apiClient, "pid", "zid") @@ -172,10 +175,7 @@ func TestUpdateZoneWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -188,30 +188,35 @@ func TestDeleteZoneWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "delete_succeeded", getFails: false, resourceState: DeleteSuccess, wantErr: false, + wantResp: true, }, { desc: "delete_failed", getFails: false, resourceState: DeleteFail, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: "", wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -222,7 +227,7 @@ func TestDeleteZoneWaitHandler(t *testing.T) { } var wantRes *dns.ZoneResponse - if !tt.getFails { + if tt.wantResp { wantRes = &dns.ZoneResponse{ Zone: &dns.Zone{ State: &tt.resourceState, @@ -240,10 +245,7 @@ func TestDeleteZoneWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -256,30 +258,35 @@ func TestCreateRecordSetWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: CreateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: CreateFail, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: "", wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -290,15 +297,13 @@ func TestCreateRecordSetWaitHandler(t *testing.T) { } var wantRes *dns.RecordSetResponse - if !tt.getFails { + if tt.wantResp { wantRes = &dns.RecordSetResponse{ Rrset: &dns.RecordSet{ State: &tt.resourceState, Id: utils.Ptr("rid"), }, } - } else { - wantRes = nil } handler := CreateRecordSetWaitHandler(context.Background(), apiClient, "pid", "zid", "rid") @@ -308,10 +313,7 @@ func TestCreateRecordSetWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -324,30 +326,35 @@ func TestUpdateRecordSetWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: UpdateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: UpdateFail, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: "", wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -358,15 +365,13 @@ func TestUpdateRecordSetWaitHandler(t *testing.T) { } var wantRes *dns.RecordSetResponse - if !tt.getFails { + if tt.wantResp { wantRes = &dns.RecordSetResponse{ Rrset: &dns.RecordSet{ State: &tt.resourceState, Id: utils.Ptr("rid"), }, } - } else { - wantRes = nil } handler := UpdateRecordSetWaitHandler(context.Background(), apiClient, "pid", "zid", "rid") @@ -376,10 +381,7 @@ func TestUpdateRecordSetWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -392,30 +394,35 @@ func TestDeleteRecordSetWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "delete_succeeded", getFails: false, resourceState: DeleteSuccess, wantErr: false, + wantResp: true, }, { desc: "delete_failed", getFails: false, resourceState: DeleteFail, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, resourceState: "", wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -426,7 +433,7 @@ func TestDeleteRecordSetWaitHandler(t *testing.T) { } var wantRes *dns.RecordSetResponse - if !tt.getFails { + if tt.wantResp { wantRes = &dns.RecordSetResponse{ Rrset: &dns.RecordSet{ State: &tt.resourceState, diff --git a/services/loadbalancer/wait/wait.go b/services/loadbalancer/wait/wait.go index e30b9caff..164f2ba07 100644 --- a/services/loadbalancer/wait/wait.go +++ b/services/loadbalancer/wait/wait.go @@ -35,76 +35,76 @@ type APIClientInterface interface { GetStatusExecute(ctx context.Context, projectId string) (*loadbalancer.StatusResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceName string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateLoadBalancerWaitHandler will wait for load balancer creation +func CreateLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceName string) *wait.AsyncActionHandler[loadbalancer.LoadBalancer] { + return wait.New(func() (waitFinished bool, response *loadbalancer.LoadBalancer, err error) { s, err := a.GetLoadBalancerExecute(ctx, projectId, instanceName) if err != nil { - return nil, false, err + return false, nil, err } if s == nil || s.Name == nil || *s.Name != instanceName || s.Status == nil { - return s, false, nil + return false, nil, nil } switch *s.Status { case InstanceStatusReady: - return s, true, nil + return true, s, nil case InstanceStatusUnspecified: - return nil, false, nil + return false, nil, nil case InstanceStatusPending: - return nil, false, nil + return false, nil, nil case InstanceStatusTerminating: - return nil, true, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusTerminating) + return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusTerminating) case InstanceStatusError: - return nil, true, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusError) + return true, s, fmt.Errorf("create failed for instance with name %s, got status %s", instanceName, InstanceStatusError) default: - return nil, true, fmt.Errorf("instance with name %s has unexpected status %s", instanceName, *s.Status) + return true, s, fmt.Errorf("instance with name %s has unexpected status %s", instanceName, *s.Status) } }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetLoadBalancerExecute(ctx, projectId, instanceId) +// DeleteLoadBalancerWaitHandler will wait for load balancer deletion +func DeleteLoadBalancerWaitHandler(ctx context.Context, a APIClientInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetLoadBalancerExecute(ctx, projectId, instanceId) if err == nil { - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusNotFound { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } // EnableLoadBalancingWaitHandler will wait for functionality to be enabled -func EnableLoadBalancingWaitHandler(ctx context.Context, a APIClientInterface, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +func EnableLoadBalancingWaitHandler(ctx context.Context, a APIClientInterface, projectId string) *wait.AsyncActionHandler[loadbalancer.StatusResponse] { + return wait.New(func() (waitFinished bool, response *loadbalancer.StatusResponse, err error) { s, err := a.GetStatusExecute(ctx, projectId) if err != nil { - return nil, false, err + return false, nil, err } if s == nil || s.Status == nil { - return s, false, nil + return false, nil, nil } switch *s.Status { case FunctionalityStatusReady: - return s, true, nil + return true, s, nil case FunctionalityStatusUnspecified: - return s, false, nil + return false, nil, nil case FunctionalityStatusDisabled: - return nil, false, nil + return false, nil, nil case FunctionalityStatusUpdating: - return nil, false, nil + return false, nil, nil case FunctionalityStatusDeleting: - return nil, true, fmt.Errorf("enabling load balancing failed for project %s, got status %s", projectId, FunctionalityStatusDeleting) + return true, s, fmt.Errorf("enabling load balancing failed for project %s, got status %s", projectId, FunctionalityStatusDeleting) case FunctionalityStatusFailed: - return nil, true, fmt.Errorf("enabling load balancing failed for project %s, got status %s", projectId, FunctionalityStatusFailed) + return true, s, fmt.Errorf("enabling load balancing failed for project %s, got status %s", projectId, FunctionalityStatusFailed) default: - return nil, true, fmt.Errorf("load balancing for project %s has unexpected status %s", projectId, *s.Status) + return true, s, fmt.Errorf("load balancing for project %s has unexpected status %s", projectId, *s.Status) } }) } diff --git a/services/loadbalancer/wait/wait_test.go b/services/loadbalancer/wait/wait_test.go index 1b6acd1f8..614eb0f4e 100644 --- a/services/loadbalancer/wait/wait_test.go +++ b/services/loadbalancer/wait/wait_test.go @@ -56,35 +56,41 @@ func TestCreateInstanceWaitHandler(t *testing.T) { instanceGetFails bool instanceStatus string wantErr bool + wantResp bool }{ { desc: "create_succeeded", instanceGetFails: false, instanceStatus: InstanceStatusReady, wantErr: false, + wantResp: true, }, { desc: "create_failed", instanceGetFails: false, instanceStatus: InstanceStatusError, wantErr: true, + wantResp: true, }, { desc: "create_failed_2", instanceGetFails: false, instanceStatus: InstanceStatusTerminating, wantErr: true, + wantResp: true, }, { desc: "instance_get_fails", instanceGetFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", instanceGetFails: false, - instanceStatus: "ANOTHER STATE", + instanceStatus: InstanceStatusPending, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -98,24 +104,21 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *loadbalancer.LoadBalancer - if (tt.instanceStatus == InstanceStatusReady) && !tt.instanceGetFails { + if tt.wantResp { wantRes = &loadbalancer.LoadBalancer{ Name: &instanceName, Status: &tt.instanceStatus, } } - handler := CreateInstanceWaitHandler(context.Background(), apiClient, "", instanceName) + handler := CreateLoadBalancerWaitHandler(context.Background(), apiClient, "", instanceName) gotRes, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -157,16 +160,13 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { instanceIsDeleted: tt.instanceIsDeleted, } - handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceName) + handler := DeleteLoadBalancerWaitHandler(context.Background(), apiClient, "", instanceName) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -177,30 +177,35 @@ func TestEnableLoadBalancingWaitHandler(t *testing.T) { functionalityStatus string functionalityStatusGetFails bool wantErr bool + wantResp bool }{ { desc: "enable_succeeded", functionalityStatus: FunctionalityStatusReady, functionalityStatusGetFails: false, wantErr: false, + wantResp: true, }, { desc: "enable_updating", functionalityStatus: FunctionalityStatusUpdating, functionalityStatusGetFails: false, wantErr: true, + wantResp: false, }, { desc: "enable_failed", functionalityStatus: FunctionalityStatusFailed, functionalityStatusGetFails: false, wantErr: true, + wantResp: true, }, { desc: "enable_failed_2", functionalityStatus: FunctionalityStatusUnspecified, functionalityStatusGetFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -211,7 +216,7 @@ func TestEnableLoadBalancingWaitHandler(t *testing.T) { } var wantRes *loadbalancer.StatusResponse - if (tt.functionalityStatus == FunctionalityStatusReady) && !tt.functionalityStatusGetFails { + if tt.wantResp { wantRes = &loadbalancer.StatusResponse{ Status: &tt.functionalityStatus, } @@ -224,7 +229,7 @@ func TestEnableLoadBalancingWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if err == nil && wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) diff --git a/services/logme/wait/wait.go b/services/logme/wait/wait.go index 4b4f0d0f5..45b77d502 100644 --- a/services/logme/wait/wait.go +++ b/services/logme/wait/wait.go @@ -29,112 +29,112 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*logme.CredentialsResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[logme.Instance] { + return wait.New(func() (waitFinished bool, response *logme.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[logme.Instance] { + return wait.New(func() (waitFinished bool, response *logme.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { if s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil || s.LastOperation.Description == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) } if *s.LastOperation.Type != InstanceTypeDelete { - return nil, false, nil + return false, nil, nil } if *s.LastOperation.State == InstanceStateSuccess { if strings.Contains(*s.LastOperation.Description, "DeleteFailed") || strings.Contains(*s.LastOperation.Description, "failed") { - return s, true, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) } - return s, true, nil + return true, nil, nil } - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusGone { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } -// CreateCredentialsWaitHandler will wait for creation -func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[logme.CredentialsResponse] { + return wait.New(func() (waitFinished bool, response *logme.CredentialsResponse, err error) { s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) if err != nil { 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } // If the request returns 404, the credentials have not been created yet if oapiErr.StatusCode == http.StatusNotFound { - return nil, false, nil + return false, nil, nil } - return nil, false, err + return false, nil, err } if *s.Id == credentialsId { - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteCredentialsWaitHandler will wait for deletion -func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { - return nil, false, err - } - return nil, true, nil +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err } - return s, false, nil + return true, nil, nil }) } diff --git a/services/logme/wait/wait_test.go b/services/logme/wait/wait_test.go index f69d339e1..c0f26779d 100644 --- a/services/logme/wait/wait_test.go +++ b/services/logme/wait/wait_test.go @@ -91,29 +91,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -128,7 +133,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *logme.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &logme.Instance{ InstanceId: &instanceId, LastOperation: &logme.LastOperation{ @@ -146,11 +151,8 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } diff := cmp.Diff(gotRes, wantRes) - if wantRes != nil && diff != "" { + if diff != "" { t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff) } }) @@ -163,29 +165,34 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -200,7 +207,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *logme.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &logme.Instance{ InstanceId: &instanceId, LastOperation: &logme.LastOperation{ @@ -218,10 +225,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -236,6 +240,7 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { resourceState string resourceDescription string wantErr bool + wantResp bool }{ { desc: "delete_succeeded", @@ -243,6 +248,7 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { deleteSucceeedsWithErrors: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "delete_failed", @@ -250,6 +256,7 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { deleteSucceeedsWithErrors: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "delete_succeeds_with_errors", @@ -258,12 +265,14 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { deleteSucceeedsWithErrors: true, resourceDescription: "Deleting resource: cf failed with error: DeleteFailed", wantErr: true, + wantResp: false, }, { desc: "get_fails", deleteSucceeedsWithErrors: false, getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -299,23 +308,27 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { getFails bool operationSucceeds bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, operationSucceeds: true, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, operationSucceeds: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -329,12 +342,10 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { } var wantRes *logme.CredentialsResponse - if !tt.getFails && tt.operationSucceeds { + if tt.wantResp { wantRes = &logme.CredentialsResponse{ Id: &credentialsId, } - } else if !tt.getFails && !tt.operationSucceeds { - wantRes = nil } handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) @@ -344,10 +355,7 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -391,28 +399,13 @@ func TestDeleteCredentialsWaitHandler(t *testing.T) { deletionSucceeds: tt.deletionSucceeds, } - var wantRes *logme.CredentialsResponse - if !tt.getFails && !tt.deletionSucceeds { - wantRes = &logme.CredentialsResponse{ - Id: &credentialsId, - } - } else if !tt.getFails && tt.deletionSucceeds { - wantRes = nil - } - handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/mariadb/wait/wait.go b/services/mariadb/wait/wait.go index 2e8d7e808..047e2ebc0 100644 --- a/services/mariadb/wait/wait.go +++ b/services/mariadb/wait/wait.go @@ -29,112 +29,112 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*mariadb.CredentialsResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[mariadb.Instance] { + return wait.New(func() (waitFinished bool, response *mariadb.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[mariadb.Instance] { + return wait.New(func() (waitFinished bool, response *mariadb.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { if s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil || s.LastOperation.Description == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) } if *s.LastOperation.Type != InstanceTypeDelete { - return nil, false, nil + return false, nil, nil } if *s.LastOperation.State == InstanceStateSuccess { if strings.Contains(*s.LastOperation.Description, "DeleteFailed") || strings.Contains(*s.LastOperation.Description, "failed") { - return s, true, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) } - return s, true, nil + return true, nil, nil } - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusGone { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } -// CreateCredentialsWaitHandler will wait for creation -func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[mariadb.CredentialsResponse] { + return wait.New(func() (waitFinished bool, response *mariadb.CredentialsResponse, err error) { s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) if err != nil { 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } // If the request returns 404, the credentials have not been created yet if oapiErr.StatusCode == http.StatusNotFound { - return nil, false, nil + return false, nil, nil } - return nil, false, err + return false, nil, err } if *s.Id == credentialsId { - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteCredentialsWaitHandler will wait for deletion -func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { - return nil, false, err - } - return nil, true, nil +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err } - return s, false, nil + return true, nil, nil }) } diff --git a/services/mariadb/wait/wait_test.go b/services/mariadb/wait/wait_test.go index b308019c1..4f4a6879f 100644 --- a/services/mariadb/wait/wait_test.go +++ b/services/mariadb/wait/wait_test.go @@ -91,29 +91,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -128,7 +133,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *mariadb.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &mariadb.Instance{ InstanceId: &instanceId, LastOperation: &mariadb.LastOperation{ @@ -146,11 +151,8 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } diff := cmp.Diff(gotRes, wantRes) - if wantRes != nil && diff != "" { + if diff != "" { t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff) } }) @@ -163,29 +165,34 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -200,7 +207,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *mariadb.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &mariadb.Instance{ InstanceId: &instanceId, LastOperation: &mariadb.LastOperation{ @@ -218,10 +225,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -281,14 +285,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -299,23 +300,27 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { getFails bool operationSucceeds bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, operationSucceeds: true, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, operationSucceeds: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -329,12 +334,10 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { } var wantRes *mariadb.CredentialsResponse - if !tt.getFails && tt.operationSucceeds { + if tt.wantResp { wantRes = &mariadb.CredentialsResponse{ Id: &credentialsId, } - } else if !tt.getFails && !tt.operationSucceeds { - wantRes = nil } handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) @@ -344,10 +347,7 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -391,28 +391,13 @@ func TestDeleteCredentialsWaitHandler(t *testing.T) { deletionSucceeds: tt.deletionSucceeds, } - var wantRes *mariadb.CredentialsResponse - if !tt.getFails && !tt.deletionSucceeds { - wantRes = &mariadb.CredentialsResponse{ - Id: &credentialsId, - } - } else if !tt.getFails && tt.deletionSucceeds { - wantRes = nil - } - handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/mongodbflex/wait/wait.go b/services/mongodbflex/wait/wait.go index b061820f6..f4ab205c2 100644 --- a/services/mongodbflex/wait/wait.go +++ b/services/mongodbflex/wait/wait.go @@ -24,75 +24,75 @@ type APIClientInstanceInterface interface { GetInstanceExecute(ctx context.Context, projectId, instanceId string) (*mongodbflex.GetInstanceResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - waitHandler := wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[mongodbflex.GetInstanceResponse] { + waitHandler := wait.New(func() (waitFinished bool, response *mongodbflex.GetInstanceResponse, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s == nil || s.Item == nil || s.Item.Id == nil || *s.Item.Id != instanceId || s.Item.Status == nil { - return s, false, nil + return false, nil, nil } switch *s.Item.Status { default: - return nil, true, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) + return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) case InstanceStateEmpty: - return nil, false, nil + return false, nil, nil case InstanceStateProcessing: - return nil, false, nil + return false, nil, nil case InstanceStateUnknown: - return nil, false, nil + return false, nil, nil case InstanceStateSuccess: - return s, true, nil + return true, s, nil case InstanceStateFailed: - return nil, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } }) return waitHandler.SetSleepBeforeWait(5 * time.Second) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[mongodbflex.GetInstanceResponse] { + return wait.New(func() (waitFinished bool, response *mongodbflex.GetInstanceResponse, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s == nil || s.Item == nil || s.Item.Id == nil || *s.Item.Id != instanceId || s.Item.Status == nil { - return s, false, nil + return false, nil, nil } switch *s.Item.Status { default: - return s, true, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) + return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) case InstanceStateEmpty: - return s, false, nil + return false, nil, nil case InstanceStateProcessing: - return s, false, nil + return false, nil, nil case InstanceStateUnknown: - return s, false, nil + return false, nil, nil case InstanceStateSuccess: - return s, true, nil + return true, s, nil case InstanceStateFailed: - return s, true, fmt.Errorf("update failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("update failed for instance with id %s", instanceId) } }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetInstanceExecute(ctx, projectId, instanceId) +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusNotFound { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } diff --git a/services/mongodbflex/wait/wait_test.go b/services/mongodbflex/wait/wait_test.go index 573f77fd3..2b3a04eb7 100644 --- a/services/mongodbflex/wait/wait_test.go +++ b/services/mongodbflex/wait/wait_test.go @@ -46,35 +46,41 @@ func TestCreateInstanceWaitHandler(t *testing.T) { instanceState string usersGetErrorStatus int wantErr bool + wantResp bool }{ { desc: "create_succeeded", instanceGetFails: false, instanceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", instanceGetFails: false, instanceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "create_failed_2", instanceGetFails: false, instanceState: InstanceStateEmpty, wantErr: true, + wantResp: false, }, { desc: "instance_get_fails", instanceGetFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", instanceGetFails: false, - instanceState: "ANOTHER STATE", + instanceState: InstanceStateProcessing, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -88,7 +94,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *mongodbflex.GetInstanceResponse - if (tt.instanceState == InstanceStateSuccess) && !tt.instanceGetFails { + if tt.wantResp { wantRes = &mongodbflex.GetInstanceResponse{ Item: &mongodbflex.InstanceSingleInstance{ Id: &instanceId, @@ -104,10 +110,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -120,35 +123,41 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { instanceGetFails bool instanceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", instanceGetFails: false, instanceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", instanceGetFails: false, instanceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "update_failed_2", instanceGetFails: false, instanceState: InstanceStateEmpty, wantErr: true, + wantResp: false, }, { desc: "get_fails", instanceGetFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", instanceGetFails: false, - instanceState: "ANOTHER STATE", + instanceState: InstanceStateProcessing, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -162,7 +171,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *mongodbflex.GetInstanceResponse - if !tt.instanceGetFails { + if tt.wantResp { wantRes = &mongodbflex.GetInstanceResponse{ Item: &mongodbflex.InstanceSingleInstance{ Id: &instanceId, @@ -178,10 +187,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -226,14 +232,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } diff --git a/services/objectstorage/wait/wait.go b/services/objectstorage/wait/wait.go index 7a4418ac9..259589615 100644 --- a/services/objectstorage/wait/wait.go +++ b/services/objectstorage/wait/wait.go @@ -15,32 +15,32 @@ type APIClientBucketInterface interface { GetBucketExecute(ctx context.Context, projectId string, bucketName string) (*objectstorage.GetBucketResponse, error) } -// CreateBucketWaitHandler will wait for creation -func CreateBucketWaitHandler(ctx context.Context, a APIClientBucketInterface, projectId, bucketName string) *wait.Handler { - waitHandler := wait.New(func() (res interface{}, done bool, err error) { +// CreateBucketWaitHandler will wait for bucket creation +func CreateBucketWaitHandler(ctx context.Context, a APIClientBucketInterface, projectId, bucketName string) *wait.AsyncActionHandler[objectstorage.GetBucketResponse] { + waitHandler := wait.New(func() (waitFinished bool, response *objectstorage.GetBucketResponse, err error) { s, err := a.GetBucketExecute(ctx, projectId, bucketName) if err != nil { - return nil, false, err + return false, nil, err } - return s, true, nil + return true, s, nil }) return waitHandler } -// DeleteBucketWaitHandler will wait for delete -func DeleteBucketWaitHandler(ctx context.Context, a APIClientBucketInterface, projectId, bucketName string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetBucketExecute(ctx, projectId, bucketName) +// DeleteBucketWaitHandler will wait for bucket deletion +func DeleteBucketWaitHandler(ctx context.Context, a APIClientBucketInterface, projectId, bucketName string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetBucketExecute(ctx, projectId, bucketName) if err == nil { - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to GenericOpenApiError") + return false, nil, fmt.Errorf("could not convert error to GenericOpenApiError") } if oapiErr.StatusCode != http.StatusNotFound { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } diff --git a/services/objectstorage/wait/wait_test.go b/services/objectstorage/wait/wait_test.go index 8187ac959..ee18a54f1 100644 --- a/services/objectstorage/wait/wait_test.go +++ b/services/objectstorage/wait/wait_test.go @@ -37,16 +37,19 @@ func TestCreateBucketWaitHandler(t *testing.T) { desc string bucketGetFails bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", bucketGetFails: false, wantErr: false, + wantResp: true, }, { desc: "get_fails", bucketGetFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -56,7 +59,7 @@ func TestCreateBucketWaitHandler(t *testing.T) { } var wantRes *objectstorage.GetBucketResponse - if !tt.bucketGetFails { + if tt.wantResp { wantRes = &objectstorage.GetBucketResponse{} } @@ -67,10 +70,7 @@ func TestCreateBucketWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -103,14 +103,11 @@ func TestDeleteBucketWaitHandler(t *testing.T) { handler := DeleteBucketWaitHandler(context.Background(), apiClient, "", "") - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } diff --git a/services/opensearch/wait/wait.go b/services/opensearch/wait/wait.go index f2bbf948f..6b2b4a998 100644 --- a/services/opensearch/wait/wait.go +++ b/services/opensearch/wait/wait.go @@ -29,112 +29,112 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*opensearch.CredentialsResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[opensearch.Instance] { + return wait.New(func() (waitFinished bool, response *opensearch.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[opensearch.Instance] { + return wait.New(func() (waitFinished bool, response *opensearch.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { if s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil || s.LastOperation.Description == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) } if *s.LastOperation.Type != InstanceTypeDelete { - return nil, false, nil + return false, nil, nil } if *s.LastOperation.State == InstanceStateSuccess { if strings.Contains(*s.LastOperation.Description, "DeleteFailed") || strings.Contains(*s.LastOperation.Description, "failed") { - return s, true, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) } - return s, true, nil + return true, nil, nil } - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusGone { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } -// CreateCredentialsWaitHandler will wait for creation -func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[opensearch.CredentialsResponse] { + return wait.New(func() (waitFinished bool, response *opensearch.CredentialsResponse, err error) { s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) if err != nil { 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } // If the request returns 404, the credentials have not been created yet if oapiErr.StatusCode == http.StatusNotFound { - return nil, false, nil + return false, nil, nil } - return nil, false, err + return false, nil, err } if *s.Id == credentialsId { - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteCredentialsWaitHandler will wait for deletion -func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { - return nil, false, err - } - return nil, true, nil +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err } - return s, false, nil + return true, nil, nil }) } diff --git a/services/opensearch/wait/wait_test.go b/services/opensearch/wait/wait_test.go index 045b8a1c2..c17c95e53 100644 --- a/services/opensearch/wait/wait_test.go +++ b/services/opensearch/wait/wait_test.go @@ -91,29 +91,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -128,7 +133,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *opensearch.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &opensearch.Instance{ InstanceId: &instanceId, LastOperation: &opensearch.LastOperation{ @@ -146,11 +151,8 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } diff := cmp.Diff(gotRes, wantRes) - if wantRes != nil && diff != "" { + if diff != "" { t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff) } }) @@ -163,29 +165,34 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -200,7 +207,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *opensearch.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &opensearch.Instance{ InstanceId: &instanceId, LastOperation: &opensearch.LastOperation{ @@ -218,10 +225,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -281,14 +285,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -299,23 +300,27 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { getFails bool operationSucceeds bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, operationSucceeds: true, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, operationSucceeds: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -329,12 +334,10 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { } var wantRes *opensearch.CredentialsResponse - if !tt.getFails && tt.operationSucceeds { + if tt.wantResp { wantRes = &opensearch.CredentialsResponse{ Id: &credentialsId, } - } else if !tt.getFails && !tt.operationSucceeds { - wantRes = nil } handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) @@ -344,10 +347,7 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -391,28 +391,13 @@ func TestDeleteCredentialsWaitHandler(t *testing.T) { deletionSucceeds: tt.deletionSucceeds, } - var wantRes *opensearch.CredentialsResponse - if !tt.getFails && !tt.deletionSucceeds { - wantRes = &opensearch.CredentialsResponse{ - Id: &credentialsId, - } - } else if !tt.getFails && tt.deletionSucceeds { - wantRes = nil - } - handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/postgresflex/wait/wait.go b/services/postgresflex/wait/wait.go index 5d8ae3a54..39c57d6ac 100644 --- a/services/postgresflex/wait/wait.go +++ b/services/postgresflex/wait/wait.go @@ -27,32 +27,32 @@ type APIClientUserInterface interface { GetUserExecute(ctx context.Context, projectId, instanceId, userId string) (*postgresflex.UserResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[postgresflex.InstanceResponse] { instanceCreated := false var instanceGetResponse *postgresflex.InstanceResponse - return wait.New(func() (res interface{}, done bool, err error) { + return wait.New(func() (waitFinished bool, response *postgresflex.InstanceResponse, err error) { if !instanceCreated { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s == nil || s.Item == nil || s.Item.Id == nil || *s.Item.Id != instanceId || s.Item.Status == nil { - return s, false, nil + return false, nil, nil } switch *s.Item.Status { default: - return nil, true, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) + return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) case InstanceStateEmpty: - return nil, false, nil + return false, nil, nil case InstanceStateProgressing: - return nil, false, nil + return false, nil, nil case InstanceStateSuccess: instanceCreated = true instanceGetResponse = s case InstanceStateFailed: - return nil, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } } @@ -60,76 +60,76 @@ func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface // To check if they are, perform a users request _, err = a.GetUsersExecute(ctx, projectId, instanceId) if err == nil { - return instanceGetResponse, true, nil + return true, instanceGetResponse, nil } 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 if !ok { - return nil, false, err + return false, nil, err } if oapiErr.StatusCode < 500 { - return nil, true, fmt.Errorf("users request after instance creation returned %d status code", oapiErr.StatusCode) + return true, instanceGetResponse, fmt.Errorf("users request after instance creation returned %d status code", oapiErr.StatusCode) } - return nil, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[postgresflex.InstanceResponse] { + return wait.New(func() (waitFinished bool, response *postgresflex.InstanceResponse, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s == nil || s.Item == nil || s.Item.Id == nil || *s.Item.Id != instanceId || s.Item.Status == nil { - return s, false, nil + return false, nil, nil } switch *s.Item.Status { default: - return s, true, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) + return true, s, fmt.Errorf("instance with id %s has unexpected status %s", instanceId, *s.Item.Status) case InstanceStateEmpty: - return s, false, nil + return false, nil, nil case InstanceStateProgressing: - return s, false, nil + return false, nil, nil case InstanceStateSuccess: - return s, true, nil + return true, s, nil case InstanceStateFailed: - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetInstanceExecute(ctx, projectId, instanceId) +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, err + return false, nil, err } if oapiErr.StatusCode != 404 { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } // DeleteUserWaitHandler will wait for delete -func DeleteUserWaitHandler(ctx context.Context, a APIClientUserInterface, projectId, instanceId, userId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - u, err := a.GetUserExecute(ctx, projectId, instanceId, userId) +func DeleteUserWaitHandler(ctx context.Context, a APIClientUserInterface, projectId, instanceId, userId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetUserExecute(ctx, projectId, instanceId, userId) if err == nil { - return u, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, err + return false, nil, err } if oapiErr.StatusCode != 404 { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } diff --git a/services/postgresflex/wait/wait_test.go b/services/postgresflex/wait/wait_test.go index 35d9ea21b..965bec5c2 100644 --- a/services/postgresflex/wait/wait_test.go +++ b/services/postgresflex/wait/wait_test.go @@ -88,29 +88,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { instanceState string usersGetErrorStatus int wantErr bool + wantResp bool }{ { desc: "create_succeeded", instanceGetFails: false, instanceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", instanceGetFails: false, instanceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "create_failed_2", instanceGetFails: false, instanceState: InstanceStateEmpty, wantErr: true, + wantResp: false, }, { desc: "instance_get_fails", instanceGetFails: true, wantErr: true, + wantResp: false, }, { desc: "users_get_fails", @@ -118,6 +123,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { instanceState: InstanceStateSuccess, usersGetErrorStatus: 500, wantErr: true, + wantResp: false, }, { desc: "users_get_fails_2", @@ -125,12 +131,14 @@ func TestCreateInstanceWaitHandler(t *testing.T) { instanceState: InstanceStateSuccess, usersGetErrorStatus: 400, wantErr: true, + wantResp: true, }, { desc: "timeout", instanceGetFails: false, - instanceState: "ANOTHER STATE", + instanceState: InstanceStateProgressing, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -145,7 +153,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *postgresflex.InstanceResponse - if (tt.instanceState == InstanceStateSuccess) && !tt.instanceGetFails && (tt.usersGetErrorStatus == 0) { + if tt.wantResp { wantRes = &postgresflex.InstanceResponse{ Item: &postgresflex.InstanceSingleInstance{ Id: &instanceId, @@ -161,10 +169,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -177,35 +182,41 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { instanceGetFails bool instanceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", instanceGetFails: false, instanceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", instanceGetFails: false, instanceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "update_failed_2", instanceGetFails: false, instanceState: InstanceStateEmpty, wantErr: true, + wantResp: false, }, { desc: "get_fails", instanceGetFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", instanceGetFails: false, - instanceState: "ANOTHER STATE", + instanceState: InstanceStateProgressing, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -219,7 +230,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *postgresflex.InstanceResponse - if !tt.instanceGetFails { + if tt.wantResp { wantRes = &postgresflex.InstanceResponse{ Item: &postgresflex.InstanceSingleInstance{ Id: &instanceId, @@ -235,10 +246,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -283,14 +291,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -333,14 +338,11 @@ func TestDeleteUserWaitHandler(t *testing.T) { handler := DeleteUserWaitHandler(context.Background(), apiClient, "", "", userId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } diff --git a/services/postgresql/wait/wait.go b/services/postgresql/wait/wait.go index a00aba247..9e04124f4 100644 --- a/services/postgresql/wait/wait.go +++ b/services/postgresql/wait/wait.go @@ -29,112 +29,112 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*postgresql.CredentialsResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[postgresql.Instance] { + return wait.New(func() (waitFinished bool, response *postgresql.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[postgresql.Instance] { + return wait.New(func() (waitFinished bool, response *postgresql.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { if s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil || s.LastOperation.Description == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) } if *s.LastOperation.Type != InstanceTypeDelete { - return nil, false, nil + return false, nil, nil } if *s.LastOperation.State == InstanceStateSuccess { if strings.Contains(*s.LastOperation.Description, "DeleteFailed") || strings.Contains(*s.LastOperation.Description, "failed") { - return s, true, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) } - return s, true, nil + return true, nil, nil } - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusGone { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } -// CreateCredentialsWaitHandler will wait for creation -func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[postgresql.CredentialsResponse] { + return wait.New(func() (waitFinished bool, response *postgresql.CredentialsResponse, err error) { s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) if err != nil { 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } // If the request returns 404, the credentials have not been created yet if oapiErr.StatusCode == http.StatusNotFound { - return nil, false, nil + return false, nil, nil } - return nil, false, err + return false, nil, err } if *s.Id == credentialsId { - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteCredentialsWaitHandler will wait for deletion -func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { - return nil, false, err - } - return nil, true, nil +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err } - return s, false, nil + return true, nil, nil }) } diff --git a/services/postgresql/wait/wait_test.go b/services/postgresql/wait/wait_test.go index 64e35dd74..502b4ca7c 100644 --- a/services/postgresql/wait/wait_test.go +++ b/services/postgresql/wait/wait_test.go @@ -85,29 +85,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -122,7 +127,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *postgresql.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &postgresql.Instance{ InstanceId: &instanceId, LastOperation: &postgresql.LastOperation{ @@ -140,11 +145,8 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } diff := cmp.Diff(gotRes, wantRes) - if wantRes != nil && diff != "" { + if diff != "" { t.Fatalf("handler gotRes = %v, want %v \n diff %s", gotRes, wantRes, diff) } }) @@ -157,29 +159,34 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -194,7 +201,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *postgresql.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &postgresql.Instance{ InstanceId: &instanceId, LastOperation: &postgresql.LastOperation{ @@ -211,10 +218,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -274,14 +278,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -292,23 +293,27 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { getFails bool operationSucceeds bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, operationSucceeds: true, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, operationSucceeds: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -322,12 +327,10 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { } var wantRes *postgresql.CredentialsResponse - if !tt.getFails && tt.operationSucceeds { + if tt.wantResp { wantRes = &postgresql.CredentialsResponse{ Id: &credentialsId, } - } else if !tt.getFails && !tt.operationSucceeds { - wantRes = nil } handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) @@ -337,10 +340,7 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -384,28 +384,13 @@ func TestDeleteCredentialsWaitHandler(t *testing.T) { deletionSucceeds: tt.deletionSucceeds, } - var wantRes *postgresql.CredentialsResponse - if !tt.getFails && !tt.deletionSucceeds { - wantRes = &postgresql.CredentialsResponse{ - Id: &credentialsId, - } - } else if !tt.getFails && tt.deletionSucceeds { - wantRes = nil - } - handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/rabbitmq/wait/wait.go b/services/rabbitmq/wait/wait.go index 0ef2f3b09..8bce340ea 100644 --- a/services/rabbitmq/wait/wait.go +++ b/services/rabbitmq/wait/wait.go @@ -29,112 +29,112 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*rabbitmq.CredentialsResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[rabbitmq.Instance] { + return wait.New(func() (waitFinished bool, response *rabbitmq.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[rabbitmq.Instance] { + return wait.New(func() (waitFinished bool, response *rabbitmq.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { if s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil || s.LastOperation.Description == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) } if *s.LastOperation.Type != InstanceTypeDelete { - return nil, false, nil + return false, nil, nil } if *s.LastOperation.State == InstanceStateSuccess { if strings.Contains(*s.LastOperation.Description, "DeleteFailed") || strings.Contains(*s.LastOperation.Description, "failed") { - return s, true, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) } - return s, true, nil + return true, nil, nil } - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusGone { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } -// CreateCredentialsWaitHandler will wait for creation -func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[rabbitmq.CredentialsResponse] { + return wait.New(func() (waitFinished bool, response *rabbitmq.CredentialsResponse, err error) { s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) if err != nil { 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } // If the request returns 404, the credentials have not been created yet if oapiErr.StatusCode == http.StatusNotFound { - return nil, false, nil + return false, nil, nil } - return nil, false, err + return false, nil, err } if *s.Id == credentialsId { - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteCredentialsWaitHandler will wait for deletion -func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { - return nil, false, err - } - return nil, true, nil +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err } - return s, false, nil + return true, nil, nil }) } diff --git a/services/rabbitmq/wait/wait_test.go b/services/rabbitmq/wait/wait_test.go index ec7dc20df..353951729 100644 --- a/services/rabbitmq/wait/wait_test.go +++ b/services/rabbitmq/wait/wait_test.go @@ -91,29 +91,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -128,7 +133,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *rabbitmq.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &rabbitmq.Instance{ InstanceId: &instanceId, LastOperation: &rabbitmq.LastOperation{ @@ -146,11 +151,8 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } diff := cmp.Diff(gotRes, wantRes) - if wantRes != nil && diff != "" { + if diff != "" { t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff) } }) @@ -163,29 +165,34 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -200,7 +207,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *rabbitmq.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &rabbitmq.Instance{ InstanceId: &instanceId, LastOperation: &rabbitmq.LastOperation{ @@ -218,10 +225,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -281,14 +285,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -299,23 +300,27 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { getFails bool operationSucceeds bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, operationSucceeds: true, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, operationSucceeds: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -329,12 +334,10 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { } var wantRes *rabbitmq.CredentialsResponse - if !tt.getFails && tt.operationSucceeds { + if tt.wantResp { wantRes = &rabbitmq.CredentialsResponse{ Id: &credentialsId, } - } else if !tt.getFails && !tt.operationSucceeds { - wantRes = nil } handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) @@ -344,10 +347,7 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -391,28 +391,13 @@ func TestDeleteCredentialsWaitHandler(t *testing.T) { deletionSucceeds: tt.deletionSucceeds, } - var wantRes *rabbitmq.CredentialsResponse - if !tt.getFails && !tt.deletionSucceeds { - wantRes = &rabbitmq.CredentialsResponse{ - Id: &credentialsId, - } - } else if !tt.getFails && tt.deletionSucceeds { - wantRes = nil - } - handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/redis/wait/wait.go b/services/redis/wait/wait.go index 22581ea72..cd10208db 100644 --- a/services/redis/wait/wait.go +++ b/services/redis/wait/wait.go @@ -29,112 +29,112 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*redis.CredentialsResponse, error) } -// CreateInstanceWaitHandler will wait for creation -func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateInstanceWaitHandler will wait for instance creation +func CreateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[redis.Instance] { + return wait.New(func() (waitFinished bool, response *redis.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("create failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeCreate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// UpdateInstanceWaitHandler will wait for update -func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// UpdateInstanceWaitHandler will wait for instance update +func UpdateInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[redis.Instance] { + return wait.New(func() (waitFinished bool, response *redis.Instance, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err != nil { - return nil, false, err + return false, nil, err } if s.InstanceId == nil || s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil { - return s, false, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) + return false, nil, fmt.Errorf("update failed for instance with id %s. The response is not valid: the instance id, the last operation type or the state are missing", instanceId) } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateSuccess { - return s, true, nil + return true, s, nil } if *s.InstanceId == instanceId && *s.LastOperation.Type == InstanceTypeUpdate && *s.LastOperation.State == InstanceStateFailed { - return s, true, fmt.Errorf("create failed for instance with id %s", instanceId) + return true, s, fmt.Errorf("create failed for instance with id %s", instanceId) } - return s, false, nil + return false, nil, nil }) } -// DeleteInstanceWaitHandler will wait for delete -func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteInstanceWaitHandler will wait for instance deletion +func DeleteInstanceWaitHandler(ctx context.Context, a APIClientInstanceInterface, projectId, instanceId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { s, err := a.GetInstanceExecute(ctx, projectId, instanceId) if err == nil { if s.LastOperation == nil || s.LastOperation.Type == nil || s.LastOperation.State == nil || s.LastOperation.Description == nil { - return s, false, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) + return false, nil, fmt.Errorf("delete failed for instance with id %s. The response is not valid: The last operation type, description or the state are missing", instanceId) } if *s.LastOperation.Type != InstanceTypeDelete { - return nil, false, nil + return false, nil, nil } if *s.LastOperation.State == InstanceStateSuccess { if strings.Contains(*s.LastOperation.Description, "DeleteFailed") || strings.Contains(*s.LastOperation.Description, "failed") { - return s, true, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) + return true, nil, fmt.Errorf("instance was deleted successfully but has errors: %s", *s.LastOperation.Description) } - return s, true, nil + return true, nil, nil } - return s, false, nil + return false, nil, nil } 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } if oapiErr.StatusCode != http.StatusGone { - return nil, false, err + return false, nil, err } - return nil, true, nil + return true, nil, nil }) } -// CreateCredentialsWaitHandler will wait for creation -func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateCredentialsWaitHandler will wait for credentials creation +func CreateCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[redis.CredentialsResponse] { + return wait.New(func() (waitFinished bool, response *redis.CredentialsResponse, err error) { s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) if err != nil { 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 if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") } // If the request returns 404, the credentials have not been created yet if oapiErr.StatusCode == http.StatusNotFound { - return nil, false, nil + return false, nil, nil } - return nil, false, err + return false, nil, err } if *s.Id == credentialsId { - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteCredentialsWaitHandler will wait for deletion -func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { - return nil, false, err - } - return nil, true, nil +// DeleteCredentialsWaitHandler will wait for credentials deletion +func DeleteCredentialsWaitHandler(ctx context.Context, a APIClientCredentialsInterface, projectId, instanceId, credentialsId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetCredentialsExecute(ctx, projectId, instanceId, credentialsId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode != http.StatusNotFound && oapiErr.StatusCode != http.StatusGone { + return false, nil, err } - return s, false, nil + return true, nil, nil }) } diff --git a/services/redis/wait/wait_test.go b/services/redis/wait/wait_test.go index 07097a180..26f13fa72 100644 --- a/services/redis/wait/wait_test.go +++ b/services/redis/wait/wait_test.go @@ -91,29 +91,34 @@ func TestCreateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -128,7 +133,7 @@ func TestCreateInstanceWaitHandler(t *testing.T) { } var wantRes *redis.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &redis.Instance{ InstanceId: &instanceId, LastOperation: &redis.LastOperation{ @@ -146,11 +151,8 @@ func TestCreateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } diff := cmp.Diff(gotRes, wantRes) - if wantRes != nil && diff != "" { + if diff != "" { t.Fatalf("handler gotRes = %+v\n want %+v\n diff = %s", gotRes, wantRes, diff) } }) @@ -163,29 +165,34 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "update_succeeded", getFails: false, resourceState: InstanceStateSuccess, wantErr: false, + wantResp: true, }, { desc: "update_failed", getFails: false, resourceState: InstanceStateFailed, wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -200,7 +207,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { } var wantRes *redis.Instance - if !tt.getFails { + if tt.wantResp { wantRes = &redis.Instance{ InstanceId: &instanceId, LastOperation: &redis.LastOperation{ @@ -218,10 +225,7 @@ func TestUpdateInstanceWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -281,14 +285,11 @@ func TestDeleteInstanceWaitHandler(t *testing.T) { handler := DeleteInstanceWaitHandler(context.Background(), apiClient, "", instanceId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 err == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, nil) - } }) } } @@ -299,23 +300,27 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { getFails bool operationSucceeds bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, operationSucceeds: true, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, operationSucceeds: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -329,12 +334,10 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { } var wantRes *redis.CredentialsResponse - if !tt.getFails && tt.operationSucceeds { + if tt.wantResp { wantRes = &redis.CredentialsResponse{ Id: &credentialsId, } - } else if !tt.getFails && !tt.operationSucceeds { - wantRes = nil } handler := CreateCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) @@ -344,10 +347,7 @@ func TestCreateCredentialsWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -391,28 +391,13 @@ func TestDeleteCredentialsWaitHandler(t *testing.T) { deletionSucceeds: tt.deletionSucceeds, } - var wantRes *redis.CredentialsResponse - if !tt.getFails && !tt.deletionSucceeds { - wantRes = &redis.CredentialsResponse{ - Id: &credentialsId, - } - } else if !tt.getFails && tt.deletionSucceeds { - wantRes = nil - } - handler := DeleteCredentialsWaitHandler(context.Background(), apiClient, "", "", credentialsId) - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/resourcemanager/wait/wait.go b/services/resourcemanager/wait/wait.go index cb54c2061..0073b9503 100644 --- a/services/resourcemanager/wait/wait.go +++ b/services/resourcemanager/wait/wait.go @@ -20,42 +20,40 @@ type APIClientInterface interface { GetProjectExecute(ctx context.Context, containerId string) (*resourcemanager.ProjectResponseWithParents, error) } -// CreateProjectWaitHandler will wait for creation -// returned interface is nil or *resourcemanager.ProjectResponseWithParents -func CreateProjectWaitHandler(ctx context.Context, a APIClientInterface, containerId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateProjectWaitHandler will wait for project creation +func CreateProjectWaitHandler(ctx context.Context, a APIClientInterface, containerId string) *wait.AsyncActionHandler[resourcemanager.ProjectResponseWithParents] { + return wait.New(func() (waitFinished bool, response *resourcemanager.ProjectResponseWithParents, err error) { p, err := a.GetProjectExecute(ctx, containerId) if err != nil { - return nil, false, err + return false, nil, err } if p.ContainerId == nil || p.LifecycleState == nil { - return p, false, fmt.Errorf("creation failed: response invalid for container id %s. Container id or LifeCycleState missing", containerId) + return false, nil, fmt.Errorf("creation failed: response invalid for container id %s. Container id or LifeCycleState missing", containerId) } if *p.ContainerId == containerId && *p.LifecycleState == ActiveState { - return p, true, nil + return true, p, nil } if *p.ContainerId == containerId && *p.LifecycleState == CreatingState { - return p, false, nil + return false, nil, nil } - return p, false, fmt.Errorf("creation failed: received project state '%s'", *p.LifecycleState) + return true, p, fmt.Errorf("creation failed: received project state '%s'", *p.LifecycleState) }) } -// DeleteProjectWaitHandler will wait for delete -// returned interface is nil or *resourcemanager.ProjectResponseWithParents -func DeleteProjectWaitHandler(ctx context.Context, a APIClientInterface, containerId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - p, err := a.GetProjectExecute(ctx, containerId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") - } - if oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden { - return nil, true, nil - } - return nil, false, err +// DeleteProjectWaitHandler will wait for project deletion +func DeleteProjectWaitHandler(ctx context.Context, a APIClientInterface, containerId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetProjectExecute(ctx, containerId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError") + } + if oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden { + return true, nil, nil } - return p, false, nil + return false, nil, err }) } diff --git a/services/resourcemanager/wait/wait_test.go b/services/resourcemanager/wait/wait_test.go index 10195c67e..0c4ddb0da 100644 --- a/services/resourcemanager/wait/wait_test.go +++ b/services/resourcemanager/wait/wait_test.go @@ -43,30 +43,35 @@ func TestCreateProjectWaitHandler(t *testing.T) { getFails bool projectState resourcemanager.LifecycleState wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, projectState: ActiveState, wantErr: false, + wantResp: true, }, { desc: "creating", getFails: false, projectState: CreatingState, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, projectState: resourcemanager.LifecycleState(""), wantErr: true, + wantResp: false, }, { desc: "unknown_state", getFails: false, projectState: resourcemanager.LifecycleState("ANOTHER STATE"), wantErr: true, + wantResp: true, }, } for _, tt := range tests { @@ -77,13 +82,11 @@ func TestCreateProjectWaitHandler(t *testing.T) { } var wantRes *resourcemanager.ProjectResponseWithParents - if !tt.getFails { + if tt.wantResp { wantRes = &resourcemanager.ProjectResponseWithParents{ LifecycleState: &tt.projectState, ContainerId: utils.Ptr("cid"), } - } else { - wantRes = nil } handler := CreateProjectWaitHandler(context.Background(), apiClient, "cid") @@ -93,10 +96,7 @@ func TestCreateProjectWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) } }) @@ -139,29 +139,13 @@ func TestDeleteProjectWaitHandler(t *testing.T) { projectState: tt.projectState, } - var wantRes *resourcemanager.ProjectResponseWithParents - if !tt.getFails && !tt.getNotFound { - wantRes = &resourcemanager.ProjectResponseWithParents{ - LifecycleState: &tt.projectState, - ContainerId: utils.Ptr("cid"), - } - } else { - wantRes = nil - } - handler := DeleteProjectWaitHandler(context.Background(), apiClient, "cid") - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } } diff --git a/services/ske/wait/wait.go b/services/ske/wait/wait.go index 49d147f81..d6356f687 100644 --- a/services/ske/wait/wait.go +++ b/services/ske/wait/wait.go @@ -33,12 +33,12 @@ type APIClientCredentialsInterface interface { GetCredentialsExecute(ctx context.Context, projectId, instanceId, credentialsId string) (*ske.CredentialsResponse, error) } -// CreateOrUpdateClusterWaitHandler will wait for creation -func CreateOrUpdateClusterWaitHandler(ctx context.Context, a APIClientClusterInterface, projectId, name string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateOrUpdateClusterWaitHandler will wait for cluster creation or update +func CreateOrUpdateClusterWaitHandler(ctx context.Context, a APIClientClusterInterface, projectId, name string) *wait.AsyncActionHandler[ske.ClusterResponse] { + return wait.New(func() (waitFinished bool, response *ske.ClusterResponse, err error) { s, err := a.GetClusterExecute(ctx, projectId, name) if err != nil { - return nil, false, err + return false, nil, err } state := *s.Status.Aggregated @@ -46,66 +46,71 @@ func CreateOrUpdateClusterWaitHandler(ctx context.Context, a APIClientClusterInt // -- alignment meeting with SKE team on 4.8.23 // The exception is when providing an invalid argus instance id, in that case the cluster will stay as "Impaired" until the SKE team solves it, but it is still usable. if state == StateUnhealthy && s.Status.Error != nil && s.Status.Error.Message != nil && *s.Status.Error.Code == InvalidArgusInstanceErrorCode { - return s, true, nil + return true, s, nil } if state == StateHealthy || state == StateHibernated { - return s, true, nil + return true, s, nil } - return s, false, nil + if state == StateFailed { + return true, s, fmt.Errorf("create failed") + } + + return false, nil, nil }) } -// DeleteClusterWaitHandler will wait for delete -func DeleteClusterWaitHandler(ctx context.Context, a APIClientClusterInterface, projectId, name string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// DeleteClusterWaitHandler will wait for cluster deletion +func DeleteClusterWaitHandler(ctx context.Context, a APIClientClusterInterface, projectId, name string) *wait.AsyncActionHandler[ske.ClustersResponse] { + return wait.New(func() (waitFinished bool, response *ske.ClustersResponse, err error) { s, err := a.GetClustersExecute(ctx, projectId) if err != nil { - return nil, false, err + return false, nil, err } items := *s.Items for i := range items { n := items[i].Name if n != nil && *n == name { - return s, false, nil + return false, nil, nil } } - return s, true, nil + return true, s, nil }) } -func CreateProjectWaitHandler(ctx context.Context, a APIClientProjectInterface, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { +// CreateOrUpdateClusterWaitHandler will wait for project creation +func CreateProjectWaitHandler(ctx context.Context, a APIClientProjectInterface, projectId string) *wait.AsyncActionHandler[ske.ProjectResponse] { + return wait.New(func() (waitFinished bool, response *ske.ProjectResponse, err error) { s, err := a.GetProjectExecute(ctx, projectId) if err != nil { - return nil, false, err + return false, nil, err } state := *s.State switch state { case StateDeleting, StateFailed: - return nil, false, fmt.Errorf("received state: %s for project Id: %s", state, projectId) + return false, nil, fmt.Errorf("received state: %s for project Id: %s", state, projectId) case StateCreated: - return s, true, nil + return true, s, nil } - return s, false, nil + return false, nil, nil }) } -// DeleteProjectWaitHandler will wait for delete -func DeleteProjectWaitHandler(ctx context.Context, a APIClientProjectInterface, projectId string) *wait.Handler { - return wait.New(func() (res interface{}, done bool, err error) { - s, err := a.GetProjectExecute(ctx, projectId) - if err != nil { - 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 - if !ok { - return nil, false, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError in delete wait handler, %w", err) - } - if oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden { - return nil, true, nil - } - return nil, false, err +// DeleteProjectWaitHandler will wait for project deletion +func DeleteProjectWaitHandler(ctx context.Context, a APIClientProjectInterface, projectId string) *wait.AsyncActionHandler[struct{}] { + return wait.New(func() (waitFinished bool, response *struct{}, err error) { + _, err = a.GetProjectExecute(ctx, projectId) + if err == nil { + return false, nil, nil + } + 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 + if !ok { + return false, nil, fmt.Errorf("could not convert error to oapierror.GenericOpenAPIError in delete wait.AsyncHandler, %w", err) + } + if oapiErr.StatusCode == http.StatusNotFound || oapiErr.StatusCode == http.StatusForbidden { + return true, nil, nil } - return s, false, nil + return false, nil, err }) } diff --git a/services/ske/wait/wait_test.go b/services/ske/wait/wait_test.go index ad41d8009..216717bbc 100644 --- a/services/ske/wait/wait_test.go +++ b/services/ske/wait/wait_test.go @@ -99,18 +99,21 @@ func TestCreateOrUpdateClusterWaitHandler(t *testing.T) { resourceState string invalidArgusInstance bool wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: StateHealthy, wantErr: false, + wantResp: true, }, { desc: "update_succeeded", getFails: false, resourceState: StateHibernated, wantErr: false, + wantResp: true, }, { desc: "unhealthy_cluster", @@ -118,22 +121,27 @@ func TestCreateOrUpdateClusterWaitHandler(t *testing.T) { resourceState: StateUnhealthy, invalidArgusInstance: true, wantErr: false, + wantResp: true, }, { - desc: "create_failed", - getFails: false, - wantErr: true, + desc: "create_failed", + getFails: false, + resourceState: StateFailed, + wantErr: true, + wantResp: true, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -148,21 +156,19 @@ func TestCreateOrUpdateClusterWaitHandler(t *testing.T) { } var wantRes *ske.ClusterResponse rs := ske.ClusterStatusState(tt.resourceState) - if !tt.getFails { + if tt.wantResp { wantRes = &ske.ClusterResponse{ Name: &name, Status: &ske.ClusterStatus{ Aggregated: &rs, }, } - } else { - wantRes = nil - } - if tt.invalidArgusInstance { - wantRes.Status.Error = &ske.RuntimeError{ - Code: utils.Ptr(string(InvalidArgusInstanceErrorCode)), - Message: utils.Ptr("invalid argus instance"), + if tt.invalidArgusInstance { + wantRes.Status.Error = &ske.RuntimeError{ + Code: utils.Ptr(string(InvalidArgusInstanceErrorCode)), + Message: utils.Ptr("invalid argus instance"), + } } } @@ -173,10 +179,7 @@ func TestCreateOrUpdateClusterWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %+v, want %+v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %+v, want %+v", gotRes, wantRes) } }) @@ -189,28 +192,33 @@ func TestCreateProjectWaitHandler(t *testing.T) { getFails bool resourceState string wantErr bool + wantResp bool }{ { desc: "create_succeeded", getFails: false, resourceState: StateCreated, wantErr: false, + wantResp: true, }, { desc: "create_failed", getFails: false, wantErr: true, + wantResp: false, }, { desc: "get_fails", getFails: true, wantErr: true, + wantResp: false, }, { desc: "timeout", getFails: false, resourceState: "ANOTHER STATE", wantErr: true, + wantResp: false, }, } for _, tt := range tests { @@ -221,7 +229,7 @@ func TestCreateProjectWaitHandler(t *testing.T) { } var wantRes *ske.ProjectResponse rs := ske.ProjectState(tt.resourceState) - if !tt.getFails { + if tt.wantResp { wantRes = &ske.ProjectResponse{ ProjectId: utils.Ptr("pid"), State: &rs, @@ -235,10 +243,7 @@ func TestCreateProjectWaitHandler(t *testing.T) { if (err != nil) != tt.wantErr { t.Fatalf("handler error = %v, wantErr %v", err, tt.wantErr) } - if wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %+v, want %+v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { + if !cmp.Equal(gotRes, wantRes) { t.Fatalf("handler gotRes = %+v, want %+v", gotRes, wantRes) } }) @@ -279,30 +284,13 @@ func TestDeleteProjectWaitHandler(t *testing.T) { resourceState: tt.resourceState, } - var wantRes *ske.ProjectResponse - if !tt.getFails && !tt.getNotFound { - rs := ske.ProjectState(tt.resourceState) - wantRes = &ske.ProjectResponse{ - ProjectId: utils.Ptr("pid"), - State: &rs, - } - } else { - wantRes = nil - } - handler := DeleteProjectWaitHandler(context.Background(), apiClient, "") - gotRes, err := handler.SetTimeout(10 * time.Millisecond).WaitWithContext(context.Background()) + _, 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 wantRes == nil && gotRes != nil { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } - if wantRes != nil && !cmp.Equal(gotRes, wantRes) { - t.Fatalf("handler gotRes = %v, want %v", gotRes, wantRes) - } }) } }