summaryrefslogtreecommitdiffstats
path: root/internal/retry/wait/kubernetes_apimachinery_wait.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/retry/wait/kubernetes_apimachinery_wait.go')
-rw-r--r--internal/retry/wait/kubernetes_apimachinery_wait.go123
1 files changed, 123 insertions, 0 deletions
diff --git a/internal/retry/wait/kubernetes_apimachinery_wait.go b/internal/retry/wait/kubernetes_apimachinery_wait.go
new file mode 100644
index 0000000..ab06e5f
--- /dev/null
+++ b/internal/retry/wait/kubernetes_apimachinery_wait.go
@@ -0,0 +1,123 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package wait is a subset of k8s.io/apimachinery to avoid conflicts
+// in dependencies (specifically, logging).
+package wait
+
+import (
+ "errors"
+ "math/rand"
+ "time"
+)
+
+// Jitter returns a time.Duration between duration and duration + maxFactor *
+// duration.
+//
+// This allows clients to avoid converging on periodic behavior. If maxFactor
+// is 0.0, a suggested default value will be chosen.
+func Jitter(duration time.Duration, maxFactor float64) time.Duration {
+ if maxFactor <= 0.0 {
+ maxFactor = 1.0
+ }
+ wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
+ return wait
+}
+
+// ErrWaitTimeout is returned when the condition exited without success.
+var ErrWaitTimeout = errors.New("timed out waiting for the condition")
+
+// ConditionFunc returns true if the condition is satisfied, or an error
+// if the loop should be aborted.
+type ConditionFunc func() (done bool, err error)
+
+// Backoff holds parameters applied to a Backoff function.
+type Backoff struct {
+ // The initial duration.
+ Duration time.Duration
+ // Duration is multiplied by factor each iteration, if factor is not zero
+ // and the limits imposed by Steps and Cap have not been reached.
+ // Should not be negative.
+ // The jitter does not contribute to the updates to the duration parameter.
+ Factor float64
+ // The sleep at each iteration is the duration plus an additional
+ // amount chosen uniformly at random from the interval between
+ // zero and `jitter*duration`.
+ Jitter float64
+ // The remaining number of iterations in which the duration
+ // parameter may change (but progress can be stopped earlier by
+ // hitting the cap). If not positive, the duration is not
+ // changed. Used for exponential backoff in combination with
+ // Factor and Cap.
+ Steps int
+ // A limit on revised values of the duration parameter. If a
+ // multiplication by the factor parameter would make the duration
+ // exceed the cap then the duration is set to the cap and the
+ // steps parameter is set to zero.
+ Cap time.Duration
+}
+
+// Step (1) returns an amount of time to sleep determined by the
+// original Duration and Jitter and (2) mutates the provided Backoff
+// to update its Steps and Duration.
+func (b *Backoff) Step() time.Duration {
+ if b.Steps < 1 {
+ if b.Jitter > 0 {
+ return Jitter(b.Duration, b.Jitter)
+ }
+ return b.Duration
+ }
+ b.Steps--
+
+ duration := b.Duration
+
+ // calculate the next step
+ if b.Factor != 0 {
+ b.Duration = time.Duration(float64(b.Duration) * b.Factor)
+ if b.Cap > 0 && b.Duration > b.Cap {
+ b.Duration = b.Cap
+ b.Steps = 0
+ }
+ }
+
+ if b.Jitter > 0 {
+ duration = Jitter(duration, b.Jitter)
+ }
+ return duration
+}
+
+// ExponentialBackoff repeats a condition check with exponential backoff.
+//
+// It repeatedly checks the condition and then sleeps, using `backoff.Step()`
+// to determine the length of the sleep and adjust Duration and Steps.
+// Stops and returns as soon as:
+// 1. the condition check returns true or an error,
+// 2. `backoff.Steps` checks of the condition have been done, or
+// 3. a sleep truncated by the cap on duration has been completed.
+// In case (1) the returned error is what the condition function returned.
+// In all other cases, ErrWaitTimeout is returned.
+func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
+ for backoff.Steps > 0 {
+ if ok, err := condition(); err != nil || ok {
+ return err
+ }
+ if backoff.Steps == 1 {
+ break
+ }
+ time.Sleep(backoff.Step())
+ }
+ return ErrWaitTimeout
+}