Skip to content

Commit 51679aa

Browse files
authored
Merge pull request #2307 from vyncint/fix-es-budget-division
fix(e2e): share the elasticsearch readiness budget between the waits
2 parents 96aea1c + 45b77bb commit 51679aa

1 file changed

Lines changed: 41 additions & 21 deletions

File tree

e2e/elasticsearch-multiversion/elasticsearch_multiversion_test.go

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,10 @@ const (
5050
// esReadyFallback applies when the binary has no deadline to derive from.
5151
esReadyFallback = 15 * time.Minute
5252

53-
// esReadyMargin is the room left before the deadline for the wait to report
54-
// its own failure. It does not cover teardown, which can outlast it.
55-
esReadyMargin = 30 * time.Second
53+
// esReadyMargin is the room left before the deadline. 30s covered the wait
54+
// reporting its own failure but not teardown, which is a stern dump, a
55+
// kubectl exec plus tar for coverage, and a kind delete.
56+
esReadyMargin = 90 * time.Second
5657
)
5758

5859
var TestTempDir string
@@ -93,21 +94,24 @@ func logContainerRestarts(t *testing.T, c common.Cluster, ctx context.Context, n
9394
}
9495
}
9596

96-
// esReadyBudget caps a wait strictly below the enclosing -timeout, so an
97-
// Elasticsearch deployment that never becomes ready fails by name instead of
98-
// letting the package binary panic and report only a goroutine dump.
99-
func esReadyBudget(t *testing.T) time.Duration {
97+
// esReadyBudget shares what is left of the -timeout between the deployments
98+
// still to check. Handing it all to the first let a slow one spend the
99+
// package's budget, leaving the rest to fail instantly under the wrong name.
100+
// The fallback is already per-wait, so it is not divided.
101+
func esReadyBudget(t *testing.T, waitsLeft int) time.Duration {
100102
deadline, ok := t.Deadline()
101103
if !ok {
102104
return esReadyFallback
103105
}
104-
return budgetWithin(time.Until(deadline))
106+
return budgetWithin(time.Until(deadline), waitsLeft)
105107
}
106108

107-
// budgetWithin never returns a non-positive duration: require.Eventually treats
108-
// one as already expired, which would fail every run instead of only stalled ones.
109-
func budgetWithin(remaining time.Duration) time.Duration {
110-
if budget := remaining - esReadyMargin; budget > 0 {
109+
// budgetWithin floors after dividing, not before. A third of a second is
110+
// positive but expires on the first tick, so flooring on the undivided
111+
// remainder would put the misattribution this split removes back at the
112+
// boundary: elasticsearch7 blamed for a package that had already run out.
113+
func budgetWithin(remaining time.Duration, waitsLeft int) time.Duration {
114+
if budget := (remaining - esReadyMargin) / time.Duration(waitsLeft); budget > time.Second {
111115
return budget
112116
}
113117
return time.Second
@@ -117,20 +121,35 @@ func TestBudgetWithin(t *testing.T) {
117121
for _, c := range []struct {
118122
name string
119123
remaining time.Duration
124+
waitsLeft int
120125
want time.Duration
121126
}{
122-
{"ample", 20 * time.Minute, 20*time.Minute - esReadyMargin},
123-
{"just above the margin", esReadyMargin + time.Second, time.Second},
124-
{"exactly the margin", esReadyMargin, time.Second},
125-
{"already past the deadline", -time.Minute, time.Second},
127+
{"shared between the waits still to come", 20 * time.Minute, 3, (20*time.Minute - esReadyMargin) / 3},
128+
{"the last wait gets what is left", 20 * time.Minute, 1, 20*time.Minute - esReadyMargin},
129+
// The floor is a second per wait, not a second shared between them.
130+
{"just above the margin", esReadyMargin + time.Second, 3, time.Second},
131+
{"a slice under a second", esReadyMargin + 2*time.Second, 3, time.Second},
132+
{"exactly the margin", esReadyMargin, 3, time.Second},
133+
{"already past the deadline", -time.Minute, 3, time.Second},
126134
} {
127135
t.Run(c.name, func(t *testing.T) {
128-
require.Equal(t, c.want, budgetWithin(c.remaining))
129-
require.Positive(t, budgetWithin(c.remaining))
136+
require.Equal(t, c.want, budgetWithin(c.remaining, c.waitsLeft))
137+
require.Positive(t, budgetWithin(c.remaining, c.waitsLeft))
130138
})
131139
}
132140
}
133141

142+
// The whole point of the split: no single wait may take the budget the others
143+
// still need.
144+
func TestBudgetLeavesRoomForTheRemainingWaits(t *testing.T) {
145+
const remaining = 20 * time.Minute
146+
147+
first := budgetWithin(remaining, 3)
148+
149+
require.Less(t, first, remaining-esReadyMargin)
150+
require.LessOrEqual(t, 3*first, remaining-esReadyMargin)
151+
}
152+
134153
func TestElasticsearch_MultiVersion(t *testing.T) {
135154
common.Initialize(t)
136155
ns := "logging"
@@ -485,15 +504,16 @@ func TestElasticsearch_MultiVersion(t *testing.T) {
485504
}
486505
common.RequireNoError(t, c.GetClient().Create(ctx, es9Deployment))
487506

488-
for _, name := range []string{"elasticsearch7", "elasticsearch8", "elasticsearch9"} {
507+
deployments := []string{"elasticsearch7", "elasticsearch8", "elasticsearch9"}
508+
for i, name := range deployments {
489509
t.Logf("Waiting for %s deployment to be ready...", name)
490-
require.Eventually(t, func() bool {
510+
require.Eventuallyf(t, func() bool {
491511
if wait.DeploymentAvailable(t, c.GetClient(), ctx, ns, name)() {
492512
return true
493513
}
494514
logContainerRestarts(t, c, ctx, ns, name)
495515
return false
496-
}, esReadyBudget(t), 10*time.Second)
516+
}, esReadyBudget(t, len(deployments)-i), 10*time.Second, "%s never became ready", name)
497517
}
498518

499519
logging := v1beta1.Logging{

0 commit comments

Comments
 (0)