Skip to content

Commit eb4c36a

Browse files
Feat/stackittpr 542 allow longer timout than default (#6056)
* feat(wait) only apply AsyncActionHandler.timeout if ctx has no deadline If the handler is configured with a timeout like 10 minutes and a user passes a context with a timeout of 20 minutes, the original solution timed out after 10 minutes instead of 20. context.WithTimeout() only sets a new deadline if the new deadline is shorter than the one in the parent context. When timing out wrap ctx.Err() into error to allow errors.Is(err, context.DeadlineExceeded). * feat(wait) write changelog, bump version * fix(wait) adjust timeouts in tests * feat(wait) add GetTimeout to AsyncActionHandler * fix(tests) check for is DeadlineExceeded instead of string comparison
1 parent 52b1898 commit eb4c36a

File tree

7 files changed

+49
-35
lines changed

7 files changed

+49
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
## Release (2026-MM-DD)
2+
- `core`: [v0.24.0](core/CHANGELOG.md#v0240)
3+
- **Bugfix:** Allow setting waiter timeouts via context, that are longer than the default timeout.
24
- `iaas`: [v1.7.1](services/iaas/CHANGELOG.md#v171)
35
- **Docs:** Extend description of `PortRange` struct
46
- `ske`: [v1.11.0](services/ske/CHANGELOG.md#v1110)
@@ -12,7 +14,6 @@
1214
- **Breaking change:** Remove `RuntimeError`
1315
- **Docs:** Extend description of `AccessScope`
1416

15-
1617
## Release (2026-03-27)
1718
- `alb`:
1819
- [v0.12.1](services/alb/CHANGELOG.md#v0121)

core/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## v0.24.0
2+
- **Bugfix:** Allow setting waiter timeouts via context, that are longer than the default timeout.
3+
14
## v0.23.0
25
- **New:** Add new `WaiterHelper` struct, which creates an `AsyncActionCheck` function based on the configuration
36

core/VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v0.23.0
1+
v0.24.0

core/wait/wait.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,16 @@ func (h *AsyncActionHandler[T]) SetThrottle(d time.Duration) *AsyncActionHandler
4646
}
4747

4848
// SetTimeout sets the duration for wait timeout.
49+
// This only has an effect, if there's no timeout/deadline set on the context.
4950
func (h *AsyncActionHandler[T]) SetTimeout(d time.Duration) *AsyncActionHandler[T] {
5051
h.timeout = d
5152
return h
5253
}
5354

55+
func (h *AsyncActionHandler[T]) GetTimeout() time.Duration {
56+
return h.timeout
57+
}
58+
5459
// SetSleepBeforeWait sets the duration for sleep before wait.
5560
func (h *AsyncActionHandler[T]) SetSleepBeforeWait(d time.Duration) *AsyncActionHandler[T] {
5661
h.sleepBeforeWait = d
@@ -70,8 +75,11 @@ func (h *AsyncActionHandler[T]) WaitWithContext(ctx context.Context) (res *T, er
7075
return nil, fmt.Errorf("throttle can't be 0")
7176
}
7277

73-
ctx, cancel := context.WithTimeout(ctx, h.timeout)
74-
defer cancel()
78+
if _, ok := ctx.Deadline(); !ok {
79+
var cancel context.CancelFunc
80+
ctx, cancel = context.WithTimeout(ctx, h.timeout)
81+
defer cancel()
82+
}
7583

7684
// Wait some seconds for the API to process the request
7785
if h.sleepBeforeWait > 0 {
@@ -101,7 +109,7 @@ func (h *AsyncActionHandler[T]) WaitWithContext(ctx context.Context) (res *T, er
101109

102110
select {
103111
case <-ctx.Done():
104-
return res, fmt.Errorf("WaitWithContext() has timed out")
112+
return res, fmt.Errorf("WaitWithContext() has timed out: %w", ctx.Err())
105113
case <-ticker.C:
106114
continue
107115
}

core/wait/wait_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ func TestWaitWithContext(t *testing.T) {
230230
handlerThrottle: 40 * time.Millisecond,
231231
handlerTimeout: 100 * time.Millisecond,
232232
handlerTempErrRetryLimit: 0,
233-
contextTimeout: 1000 * time.Millisecond,
233+
contextTimeout: 100 * time.Millisecond,
234234
wantCheckFnNumberCalls: 3,
235235
wantErr: true,
236236
},
@@ -269,7 +269,7 @@ func TestWaitWithContext(t *testing.T) {
269269
handlerThrottle: 40 * time.Millisecond,
270270
handlerTimeout: 100 * time.Millisecond,
271271
handlerTempErrRetryLimit: 0,
272-
contextTimeout: 1000 * time.Millisecond,
272+
contextTimeout: 100 * time.Millisecond,
273273
wantCheckFnNumberCalls: 0,
274274
wantErr: true,
275275
},

services/cdn/v1api/wait/wait_test.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package wait
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67
"strings"
78
"testing"
@@ -119,7 +120,7 @@ func TestCreateDistributionWaitHandler(t *testing.T) {
119120
distributionId: distributionId,
120121
expectedDistribution: nil,
121122
errCheck: func(t *testing.T, err error) {
122-
if err.Error() == "WaitWithContext() has timed out" {
123+
if errors.Is(err, context.DeadlineExceeded) {
123124
return
124125
}
125126
t.Fatalf("Unexpected error: %v", err)
@@ -133,7 +134,7 @@ func TestCreateDistributionWaitHandler(t *testing.T) {
133134
distributionId: distributionId,
134135
expectedDistribution: nil,
135136
errCheck: func(t *testing.T, err error) {
136-
if err.Error() == "WaitWithContext() has timed out" {
137+
if errors.Is(err, context.DeadlineExceeded) {
137138
return
138139
}
139140
t.Fatalf("Unexpected error: %v", err)
@@ -223,7 +224,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
223224
projectId: projectId,
224225
distributionId: distributionId,
225226
errCheck: func(t *testing.T, err error) {
226-
if err.Error() == "WaitWithContext() has timed out" {
227+
if errors.Is(err, context.DeadlineExceeded) {
227228
return
228229
}
229230
t.Fatalf("Unexpected error: %v", err)
@@ -236,7 +237,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
236237
projectId: projectId,
237238
distributionId: distributionId,
238239
errCheck: func(t *testing.T, err error) {
239-
if err.Error() == "WaitWithContext() has timed out" {
240+
if errors.Is(err, context.DeadlineExceeded) {
240241
return
241242
}
242243
t.Fatalf("Unexpected error: %v", err)
@@ -249,7 +250,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
249250
projectId: projectId,
250251
distributionId: distributionId,
251252
errCheck: func(t *testing.T, err error) {
252-
if err.Error() == "WaitWithContext() has timed out" {
253+
if errors.Is(err, context.DeadlineExceeded) {
253254
return
254255
}
255256
t.Fatalf("Unexpected error: %v", err)
@@ -262,7 +263,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
262263
projectId: projectId,
263264
distributionId: distributionId,
264265
errCheck: func(t *testing.T, err error) {
265-
if err.Error() == "WaitWithContext() has timed out" {
266+
if errors.Is(err, context.DeadlineExceeded) {
266267
return
267268
}
268269
t.Fatalf("Unexpected error: %v", err)
@@ -275,7 +276,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
275276
projectId: projectId,
276277
distributionId: distributionId,
277278
errCheck: func(t *testing.T, err error) {
278-
if err.Error() == "WaitWithContext() has timed out" {
279+
if errors.Is(err, context.DeadlineExceeded) {
279280
return
280281
}
281282
t.Fatalf("Unexpected error: %v", err)
@@ -348,7 +349,7 @@ func TestUpdateDistributionWaitHandler(t *testing.T) {
348349
distributionId: distributionId,
349350
expectedDistribution: nil,
350351
errCheck: func(t *testing.T, err error) {
351-
if err.Error() == "WaitWithContext() has timed out" {
352+
if errors.Is(err, context.DeadlineExceeded) {
352353
return
353354
}
354355
t.Fatalf("Unexpected error: %v", err)
@@ -461,7 +462,7 @@ func TestCreateCustomDomainWaitHandler(t *testing.T) {
461462
customDomain: customDomain,
462463
expectedCustomDomain: nil,
463464
errCheck: func(t *testing.T, err error) {
464-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
465+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
465466
return
466467
}
467468
t.Fatalf("Unexpected error: %v", err)
@@ -572,7 +573,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
572573
distributionId: distributionId,
573574
customDomain: customDomain,
574575
errCheck: func(t *testing.T, err error) {
575-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
576+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
576577
return
577578
}
578579
t.Fatalf("Unexpected error: %v", err)
@@ -586,7 +587,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
586587
distributionId: distributionId,
587588
customDomain: customDomain,
588589
errCheck: func(t *testing.T, err error) {
589-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
590+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
590591
return
591592
}
592593
t.Fatalf("Unexpected error: %v", err)
@@ -600,7 +601,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
600601
distributionId: distributionId,
601602
customDomain: customDomain,
602603
errCheck: func(t *testing.T, err error) {
603-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
604+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
604605
return
605606
}
606607
t.Fatalf("Unexpected error: %v", err)
@@ -614,7 +615,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
614615
distributionId: distributionId,
615616
customDomain: customDomain,
616617
errCheck: func(t *testing.T, err error) {
617-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
618+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
618619
return
619620
}
620621
t.Fatalf("Unexpected error: %v", err)
@@ -628,7 +629,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
628629
distributionId: distributionId,
629630
customDomain: customDomain,
630631
errCheck: func(t *testing.T, err error) {
631-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
632+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
632633
return
633634
}
634635
t.Fatalf("Unexpected error: %v", err)

services/cdn/wait/wait_test.go

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package wait
22

33
import (
44
"context"
5+
"errors"
56
"net/http"
67
"strings"
78
"testing"
@@ -115,7 +116,7 @@ func TestCreateDistributionWaitHandler(t *testing.T) {
115116
distributionId: distributionId,
116117
expectedDistribution: nil,
117118
errCheck: func(t *testing.T, err error) {
118-
if err.Error() == "WaitWithContext() has timed out" {
119+
if errors.Is(err, context.DeadlineExceeded) {
119120
return
120121
}
121122
t.Fatalf("Unexpected error: %v", err)
@@ -129,7 +130,7 @@ func TestCreateDistributionWaitHandler(t *testing.T) {
129130
distributionId: distributionId,
130131
expectedDistribution: nil,
131132
errCheck: func(t *testing.T, err error) {
132-
if err.Error() == "WaitWithContext() has timed out" {
133+
if errors.Is(err, context.DeadlineExceeded) {
133134
return
134135
}
135136
t.Fatalf("Unexpected error: %v", err)
@@ -219,7 +220,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
219220
projectId: projectId,
220221
distributionId: distributionId,
221222
errCheck: func(t *testing.T, err error) {
222-
if err.Error() == "WaitWithContext() has timed out" {
223+
if errors.Is(err, context.DeadlineExceeded) {
223224
return
224225
}
225226
t.Fatalf("Unexpected error: %v", err)
@@ -236,7 +237,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
236237
projectId: projectId,
237238
distributionId: distributionId,
238239
errCheck: func(t *testing.T, err error) {
239-
if err.Error() == "WaitWithContext() has timed out" {
240+
if errors.Is(err, context.DeadlineExceeded) {
240241
return
241242
}
242243
t.Fatalf("Unexpected error: %v", err)
@@ -253,7 +254,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
253254
projectId: projectId,
254255
distributionId: distributionId,
255256
errCheck: func(t *testing.T, err error) {
256-
if err.Error() == "WaitWithContext() has timed out" {
257+
if errors.Is(err, context.DeadlineExceeded) {
257258
return
258259
}
259260
t.Fatalf("Unexpected error: %v", err)
@@ -270,7 +271,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
270271
projectId: projectId,
271272
distributionId: distributionId,
272273
errCheck: func(t *testing.T, err error) {
273-
if err.Error() == "WaitWithContext() has timed out" {
274+
if errors.Is(err, context.DeadlineExceeded) {
274275
return
275276
}
276277
t.Fatalf("Unexpected error: %v", err)
@@ -287,7 +288,7 @@ func TestDeleteDistributionWaitHandler(t *testing.T) {
287288
projectId: projectId,
288289
distributionId: distributionId,
289290
errCheck: func(t *testing.T, err error) {
290-
if err.Error() == "WaitWithContext() has timed out" {
291+
if errors.Is(err, context.DeadlineExceeded) {
291292
return
292293
}
293294
t.Fatalf("Unexpected error: %v", err)
@@ -360,7 +361,7 @@ func TestUpdateDistributionWaitHandler(t *testing.T) {
360361
distributionId: distributionId,
361362
expectedDistribution: nil,
362363
errCheck: func(t *testing.T, err error) {
363-
if err.Error() == "WaitWithContext() has timed out" {
364+
if errors.Is(err, context.DeadlineExceeded) {
364365
return
365366
}
366367
t.Fatalf("Unexpected error: %v", err)
@@ -472,7 +473,7 @@ func TestCreateCustomDomainWaitHandler(t *testing.T) {
472473
customDomain: customDomain,
473474
expectedCustomDomain: nil,
474475
errCheck: func(t *testing.T, err error) {
475-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
476+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
476477
return
477478
}
478479
t.Fatalf("Unexpected error: %v", err)
@@ -580,7 +581,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
580581
distributionId: distributionId,
581582
customDomain: customDomain,
582583
errCheck: func(t *testing.T, err error) {
583-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
584+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
584585
return
585586
}
586587
t.Fatalf("Unexpected error: %v", err)
@@ -597,7 +598,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
597598
distributionId: distributionId,
598599
customDomain: customDomain,
599600
errCheck: func(t *testing.T, err error) {
600-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
601+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
601602
return
602603
}
603604
t.Fatalf("Unexpected error: %v", err)
@@ -614,7 +615,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
614615
distributionId: distributionId,
615616
customDomain: customDomain,
616617
errCheck: func(t *testing.T, err error) {
617-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
618+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
618619
return
619620
}
620621
t.Fatalf("Unexpected error: %v", err)
@@ -631,7 +632,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
631632
distributionId: distributionId,
632633
customDomain: customDomain,
633634
errCheck: func(t *testing.T, err error) {
634-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
635+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
635636
return
636637
}
637638
t.Fatalf("Unexpected error: %v", err)
@@ -648,7 +649,7 @@ func TestDeleteCustomDomainHandler(t *testing.T) {
648649
distributionId: distributionId,
649650
customDomain: customDomain,
650651
errCheck: func(t *testing.T, err error) {
651-
if err != nil && err.Error() == "WaitWithContext() has timed out" {
652+
if err != nil && errors.Is(err, context.DeadlineExceeded) {
652653
return
653654
}
654655
t.Fatalf("Unexpected error: %v", err)

0 commit comments

Comments
 (0)