mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-07 20:21:20 +00:00
Return immediately when controllers/pods are committed
Add client waiting conditions.
This commit is contained in:
19
pkg/util/wait/doc.go
Normal file
19
pkg/util/wait/doc.go
Normal file
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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 provides tools for polling or listening for changes
|
||||
// to a condition.
|
||||
package wait
|
83
pkg/util/wait/wait.go
Normal file
83
pkg/util/wait/wait.go
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 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)
|
||||
|
||||
// Poll tries a condition func until it returns true, an error, or the
|
||||
// wait channel is closed. Will always poll at least once.
|
||||
func Poll(interval time.Duration, cycles int, condition ConditionFunc) error {
|
||||
return WaitFor(poller(interval, cycles), condition)
|
||||
}
|
||||
|
||||
// WaitFunc creates a channel that receives an item every time a test
|
||||
// should be executed and is closed when the last test should be invoked.
|
||||
type WaitFunc func() <-chan struct{}
|
||||
|
||||
// WaitFor implements the looping for a wait.
|
||||
func WaitFor(wait WaitFunc, c ConditionFunc) error {
|
||||
w := wait()
|
||||
for {
|
||||
_, open := <-w
|
||||
ok, err := c()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if ok {
|
||||
return nil
|
||||
}
|
||||
if !open {
|
||||
break
|
||||
}
|
||||
}
|
||||
return ErrWaitTimeout
|
||||
}
|
||||
|
||||
// poller returns a WaitFunc that will send to the channel every
|
||||
// interval until at most cycles * interval has elapsed and then
|
||||
// close the channel. Over very short intervals you may receive
|
||||
// no ticks before being closed.
|
||||
func poller(interval time.Duration, cycles int) WaitFunc {
|
||||
return WaitFunc(func() <-chan struct{} {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
tick := time.NewTicker(interval)
|
||||
defer tick.Stop()
|
||||
after := time.After(interval * time.Duration(cycles))
|
||||
for {
|
||||
select {
|
||||
case <-tick.C:
|
||||
ch <- struct{}{}
|
||||
case <-after:
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
})
|
||||
}
|
125
pkg/util/wait/wait_test.go
Normal file
125
pkg/util/wait/wait_test.go
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestPoller(t *testing.T) {
|
||||
w := poller(time.Millisecond, 2)
|
||||
ch := w()
|
||||
count := 0
|
||||
DRAIN:
|
||||
for {
|
||||
select {
|
||||
case _, open := <-ch:
|
||||
if !open {
|
||||
break DRAIN
|
||||
}
|
||||
count++
|
||||
case <-time.After(time.Millisecond * 5):
|
||||
t.Errorf("unexpected timeout after poll")
|
||||
}
|
||||
}
|
||||
if count > 3 {
|
||||
t.Errorf("expected up to three values, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
func fakeTicker(count int) WaitFunc {
|
||||
return func() <-chan struct{} {
|
||||
ch := make(chan struct{})
|
||||
go func() {
|
||||
for i := 0; i < count; i++ {
|
||||
ch <- struct{}{}
|
||||
}
|
||||
close(ch)
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
}
|
||||
|
||||
func TestPoll(t *testing.T) {
|
||||
invocations := 0
|
||||
f := ConditionFunc(func() (bool, error) {
|
||||
invocations++
|
||||
return true, nil
|
||||
})
|
||||
if err := Poll(time.Microsecond, 1, f); err != nil {
|
||||
t.Fatalf("unexpected error %v", err)
|
||||
}
|
||||
if invocations == 0 {
|
||||
t.Errorf("Expected at least one invocation, got zero")
|
||||
}
|
||||
}
|
||||
|
||||
func TestWaitFor(t *testing.T) {
|
||||
var invocations int
|
||||
testCases := map[string]struct {
|
||||
F ConditionFunc
|
||||
Ticks int
|
||||
Invoked int
|
||||
Err bool
|
||||
}{
|
||||
"invoked once": {
|
||||
ConditionFunc(func() (bool, error) {
|
||||
invocations++
|
||||
return true, nil
|
||||
}),
|
||||
2,
|
||||
1,
|
||||
false,
|
||||
},
|
||||
"invoked and returns a timeout": {
|
||||
ConditionFunc(func() (bool, error) {
|
||||
invocations++
|
||||
return false, nil
|
||||
}),
|
||||
2,
|
||||
3,
|
||||
true,
|
||||
},
|
||||
"returns immediately on error": {
|
||||
ConditionFunc(func() (bool, error) {
|
||||
invocations++
|
||||
return false, errors.New("test")
|
||||
}),
|
||||
2,
|
||||
1,
|
||||
true,
|
||||
},
|
||||
}
|
||||
for k, c := range testCases {
|
||||
invocations = 0
|
||||
ticker := fakeTicker(c.Ticks)
|
||||
err := WaitFor(ticker, c.F)
|
||||
switch {
|
||||
case c.Err && err == nil:
|
||||
t.Errorf("%s: Expected error, got nil", k)
|
||||
continue
|
||||
case !c.Err && err != nil:
|
||||
t.Errorf("%s: Expected no error, got: %#v", k, err)
|
||||
continue
|
||||
}
|
||||
if invocations != c.Invoked {
|
||||
t.Errorf("%s: Expected %d invocations, called %d", k, c.Invoked, invocations)
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user