Skip to content

Commit a819708

Browse files
committed
improve informer test
1 parent 3891b99 commit a819708

File tree

2 files changed

+40
-17
lines changed

2 files changed

+40
-17
lines changed

lib/kubeclient/informer.rb

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ def initialize(client, resource_name, reconcile_timeout: 15 * 60, logger: nil)
88
@logger = logger
99
@cache = nil
1010
@started = nil
11+
@stopped = false
1112
@watching = []
1213
end
1314

@@ -21,22 +22,34 @@ def watch(&block)
2122

2223
# not implicit so users know they have to `stop`
2324
def start_worker
25+
@stopped = false
2426
@worker = Thread.new do
2527
loop do
26-
fill_cache
27-
watch_to_update_cache
28-
rescue StandardError => e
29-
# need to keep retrying since we work in the background
30-
@logger&.error("ignoring error during background work #{e}")
31-
ensure
32-
sleep(1) # do not overwhelm the api-server if we are somehow broken
28+
begin
29+
fill_cache
30+
watch_to_update_cache
31+
rescue StandardError => e
32+
# need to keep retrying since we work in the background
33+
@logger&.error("ignoring error during background work #{e}")
34+
ensure
35+
sleep(1) # do not overwhelm the api-server if we are somehow broken
36+
end
37+
break if @stopped
3338
end
3439
end
35-
sleep(0.01) until @cache
40+
sleep(0.01) until @cache || @stopped
3641
end
3742

3843
def stop_worker
39-
@worker&.kill # TODO: be nicer ?
44+
@stopped = true
45+
[@waiter, @worker].compact.each do |thread|
46+
begin
47+
thread.run # cancel sleep so either the loop sleep or the timeout sleep are interrupted
48+
rescue ThreadError
49+
# thread was already dead
50+
end
51+
thread.join
52+
end
4053
end
4154

4255
private
@@ -69,7 +82,7 @@ def watch_to_update_cache
6982
stop_reason = 'disconnect'
7083

7184
# stop watcher without using timeout
72-
Thread.new do
85+
@waiter = Thread.new do
7386
sleep(@reconcile_timeout)
7487
stop_reason = 'reconcile'
7588
@watcher.finish
@@ -88,6 +101,13 @@ def watch_to_update_cache
88101
@watching.each { |q| q << notice }
89102
end
90103
@logger&.info("watch restarted: #{stop_reason}")
104+
105+
# wake the waiter unless it's dead so it does not hang around
106+
begin
107+
@waiter.run
108+
rescue ThreadError # rubocop:disable Lint/SuppressedException
109+
end
110+
@waiter.join
91111
end
92112
end
93113
end

test/test_informer.rb

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@
44
require 'stringio'
55
require 'logger'
66

7-
# tests with_retries in kubeclient.rb
8-
class RetryTest < MiniTest::Test
7+
class TestInformer < MiniTest::Test
98
def setup
109
super
1110
skip if RUBY_ENGINE == 'truffleruby' # TODO: race condition in truffle-ruby fails random tests
@@ -87,14 +86,14 @@ def test_restarts_on_error
8786
status: 200
8887
)
8988
slept = []
90-
informer.stubs(:sleep).with { |x| slept << x; sleep(0.01) }
89+
informer.stubs(:sleep).with { |x| slept << x; sleep(0.02) }
9190

9291
with_worker do
9392
assert_equal(['a'], informer.list.map { |p| p.metadata.name })
94-
sleep(0.05)
93+
sleep(0.2) # should give us 5+ restarts (each timeout is 1 sleep and 1 sleep before restart)
9594
end
9695

97-
assert slept.size >= 2, slept
96+
assert slept.size >= 4, slept
9897
assert_requested(list, at_least_times: 2)
9998
assert_requested(watch, at_least_times: 2)
10099
end
@@ -131,10 +130,14 @@ def test_can_watch_watches
131130
def test_timeout
132131
timeout = 0.1
133132
informer.instance_variable_set(:@reconcile_timeout, timeout)
134-
stub_list
133+
list = stub_list
135134
Kubeclient::Common::WatchStream.any_instance.expects(:finish)
136-
stub_request(:get, %r{/v1/watch/pods})
135+
watch = stub_request(:get, %r{/v1/watch/pods})
136+
137137
with_worker { sleep(timeout * 1.9) }
138+
139+
assert_requested(list)
140+
assert_requested(watch)
138141
end
139142

140143
private

0 commit comments

Comments
 (0)