fluent-plugin-kubernetes_metadata_filter-rh 2.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +57 -0
  3. data/.gitignore +19 -0
  4. data/.rubocop.yml +57 -0
  5. data/Gemfile +9 -0
  6. data/Gemfile.lock +156 -0
  7. data/LICENSE.txt +201 -0
  8. data/README.md +253 -0
  9. data/Rakefile +41 -0
  10. data/fluent-plugin-kubernetes_metadata_filter.gemspec +34 -0
  11. data/lib/fluent/plugin/filter_kubernetes_metadata.rb +378 -0
  12. data/lib/fluent/plugin/kubernetes_metadata_cache_strategy.rb +102 -0
  13. data/lib/fluent/plugin/kubernetes_metadata_common.rb +120 -0
  14. data/lib/fluent/plugin/kubernetes_metadata_stats.rb +46 -0
  15. data/lib/fluent/plugin/kubernetes_metadata_util.rb +40 -0
  16. data/lib/fluent/plugin/kubernetes_metadata_watch_namespaces.rb +154 -0
  17. data/lib/fluent/plugin/kubernetes_metadata_watch_pods.rb +172 -0
  18. data/test/cassettes/invalid_api_server_config.yml +53 -0
  19. data/test/cassettes/kubernetes_docker_metadata_annotations.yml +205 -0
  20. data/test/cassettes/kubernetes_docker_metadata_dotted_labels.yml +197 -0
  21. data/test/cassettes/kubernetes_get_api_v1.yml +193 -0
  22. data/test/cassettes/kubernetes_get_api_v1_using_token.yml +195 -0
  23. data/test/cassettes/kubernetes_get_namespace_default.yml +69 -0
  24. data/test/cassettes/kubernetes_get_namespace_default_using_token.yml +71 -0
  25. data/test/cassettes/kubernetes_get_pod.yml +146 -0
  26. data/test/cassettes/kubernetes_get_pod_using_token.yml +148 -0
  27. data/test/cassettes/metadata_from_tag_and_journald_fields.yml +153 -0
  28. data/test/cassettes/metadata_from_tag_journald_and_kubernetes_fields.yml +285 -0
  29. data/test/cassettes/valid_kubernetes_api_server.yml +55 -0
  30. data/test/cassettes/valid_kubernetes_api_server_using_token.yml +57 -0
  31. data/test/helper.rb +82 -0
  32. data/test/plugin/test.token +1 -0
  33. data/test/plugin/test_cache_stats.rb +33 -0
  34. data/test/plugin/test_cache_strategy.rb +194 -0
  35. data/test/plugin/test_filter_kubernetes_metadata.rb +1012 -0
  36. data/test/plugin/test_utils.rb +56 -0
  37. data/test/plugin/test_watch_namespaces.rb +245 -0
  38. data/test/plugin/test_watch_pods.rb +344 -0
  39. data/test/plugin/watch_test.rb +74 -0
  40. metadata +269 -0
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Fluentd Kubernetes Metadata Filter Plugin - Enrich Fluentd events with
5
+ # Kubernetes metadata
6
+ #
7
+ # Copyright 2015 Red Hat, Inc.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ class KubernetesMetadataCacheStatsTest < Test::Unit::TestCase
22
+ include KubernetesMetadata::Util
23
+
24
+ def setup
25
+ @time_fields = ['time']
26
+ @internal_time = Time.now
27
+ end
28
+
29
+ test '#create_time_from_record when time is empty' do
30
+ record = { 'time' => ' ' }
31
+ assert_equal(@internal_time.to_i, create_time_from_record(record, @internal_time).to_i)
32
+ end
33
+ test '#create_time_from_record when time is nil' do
34
+ record = {}
35
+ assert_equal(@internal_time.to_i, create_time_from_record(record, @internal_time).to_i)
36
+ end
37
+
38
+ test '#create_time_from_record when time is an integer' do
39
+ exp_time = Time.now
40
+ record = { 'time' => exp_time.to_i }
41
+ assert_equal(exp_time.to_i, create_time_from_record(record, @internal_time).to_i)
42
+ end
43
+
44
+ test '#create_time_from_record when time is a string' do
45
+ exp_time = Time.now
46
+ record = { 'time' => exp_time.to_s }
47
+ assert_equal(exp_time.to_i, create_time_from_record(record, @internal_time).to_i)
48
+ end
49
+
50
+ test '#create_time_from_record when timefields include journal time fields' do
51
+ @time_fields = ['_SOURCE_REALTIME_TIMESTAMP']
52
+ exp_time = Time.now
53
+ record = { '_SOURCE_REALTIME_TIMESTAMP' => exp_time.to_i.to_s }
54
+ assert_equal(Time.at(exp_time.to_i / 1_000_000, exp_time.to_i % 1_000_000).to_i, create_time_from_record(record, @internal_time).to_i)
55
+ end
56
+ end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Fluentd Kubernetes Metadata Filter Plugin - Enrich Fluentd events with
5
+ # Kubernetes metadata
6
+ #
7
+ # Copyright 2015 Red Hat, Inc.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ require_relative '../helper'
22
+ require_relative 'watch_test'
23
+
24
+ class WatchNamespacesTestTest < WatchTest
25
+ include KubernetesMetadata::WatchNamespaces
26
+
27
+ setup do
28
+ @initial = {
29
+ kind: 'NamespaceList',
30
+ metadata: { resourceVersion: '123' },
31
+ items: [
32
+ {
33
+ metadata: {
34
+ name: 'initial',
35
+ uid: 'initial_uid'
36
+ }
37
+ },
38
+ {
39
+ metadata: {
40
+ name: 'modified',
41
+ uid: 'modified_uid'
42
+ }
43
+ }
44
+ ]
45
+ }
46
+
47
+ @created = {
48
+ type: 'CREATED',
49
+ object: {
50
+ metadata: {
51
+ name: 'created',
52
+ uid: 'created_uid'
53
+ }
54
+ }
55
+ }
56
+ @modified = {
57
+ type: 'MODIFIED',
58
+ object: {
59
+ metadata: {
60
+ name: 'foo',
61
+ uid: 'modified_uid'
62
+ }
63
+ }
64
+ }
65
+ @deleted = {
66
+ type: 'DELETED',
67
+ object: {
68
+ metadata: {
69
+ name: 'deleteme',
70
+ uid: 'deleted_uid'
71
+ }
72
+ }
73
+ }
74
+ @error = {
75
+ type: 'ERROR',
76
+ object: {
77
+ message: 'some error message'
78
+ }
79
+ }
80
+ @gone = {
81
+ type: 'ERROR',
82
+ object: {
83
+ code: 410,
84
+ kind: 'Status',
85
+ message: 'too old resource version: 123 (391079)',
86
+ metadata: {
87
+ name: 'gone',
88
+ namespace: 'gone',
89
+ uid: 'gone_uid'
90
+ },
91
+ reason: 'Gone'
92
+ }
93
+ }
94
+ end
95
+
96
+ test 'namespace list caches namespaces' do
97
+ @client.stub :get_namespaces, @initial do
98
+ process_namespace_watcher_notices(start_namespace_watch)
99
+ assert_equal(true, @namespace_cache.key?('initial_uid'))
100
+ assert_equal(true, @namespace_cache.key?('modified_uid'))
101
+ assert_equal(2, @stats[:namespace_cache_host_updates])
102
+ end
103
+ end
104
+
105
+ test 'namespace list caches namespaces and watch updates' do
106
+ orig_env_val = ENV['K8S_NODE_NAME']
107
+ ENV['K8S_NODE_NAME'] = 'aNodeName'
108
+ @client.stub :get_namespaces, @initial do
109
+ @client.stub :watch_namespaces, [@modified] do
110
+ process_namespace_watcher_notices(start_namespace_watch)
111
+ assert_equal(2, @stats[:namespace_cache_host_updates])
112
+ assert_equal(1, @stats[:namespace_cache_watch_updates])
113
+ end
114
+ end
115
+ ENV['K8S_NODE_NAME'] = orig_env_val
116
+ end
117
+
118
+ test 'namespace watch ignores CREATED' do
119
+ @client.stub :watch_namespaces, [@created] do
120
+ process_namespace_watcher_notices(start_namespace_watch)
121
+ assert_equal(false, @namespace_cache.key?('created_uid'))
122
+ assert_equal(1, @stats[:namespace_cache_watch_ignored])
123
+ end
124
+ end
125
+
126
+ test 'namespace watch ignores MODIFIED when info not in cache' do
127
+ @client.stub :watch_namespaces, [@modified] do
128
+ process_namespace_watcher_notices(start_namespace_watch)
129
+ assert_equal(false, @namespace_cache.key?('modified_uid'))
130
+ assert_equal(1, @stats[:namespace_cache_watch_misses])
131
+ end
132
+ end
133
+
134
+ test 'namespace watch updates cache when MODIFIED is received and info is cached' do
135
+ @namespace_cache['modified_uid'] = {}
136
+ @client.stub :watch_namespaces, [@modified] do
137
+ process_namespace_watcher_notices(start_namespace_watch)
138
+ assert_equal(true, @namespace_cache.key?('modified_uid'))
139
+ assert_equal(1, @stats[:namespace_cache_watch_updates])
140
+ end
141
+ end
142
+
143
+ test 'namespace watch ignores DELETED' do
144
+ @namespace_cache['deleted_uid'] = {}
145
+ @client.stub :watch_namespaces, [@deleted] do
146
+ process_namespace_watcher_notices(start_namespace_watch)
147
+ assert_equal(true, @namespace_cache.key?('deleted_uid'))
148
+ assert_equal(1, @stats[:namespace_cache_watch_deletes_ignored])
149
+ end
150
+ end
151
+
152
+ test 'namespace watch raises Fluent::UnrecoverableError when cannot re-establish connection to k8s API server' do
153
+ # Stub start_namespace_watch to simulate initial successful connection to API server
154
+ stub(self).start_namespace_watch
155
+ # Stub watch_namespaces to simluate not being able to set up watch connection to API server
156
+ stub(@client).watch_namespaces { raise }
157
+ @client.stub :get_namespaces, @initial do
158
+ assert_raise Fluent::UnrecoverableError do
159
+ set_up_namespace_thread
160
+ end
161
+ end
162
+ assert_equal(3, @stats[:namespace_watch_failures])
163
+ assert_equal(2, Thread.current[:namespace_watch_retry_count])
164
+ assert_equal(4, Thread.current[:namespace_watch_retry_backoff_interval])
165
+ assert_nil(@stats[:namespace_watch_error_type_notices])
166
+ end
167
+
168
+ test 'namespace watch resets watch retry count when exceptions are encountered and connection to k8s API server is re-established' do
169
+ @client.stub :get_namespaces, @initial do
170
+ @client.stub :watch_namespaces, [[@created, @exception_raised]] do
171
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
172
+ # no unrecoverable error was thrown during this period of time.
173
+ assert_raise Timeout::Error.new('execution expired') do
174
+ Timeout.timeout(3) do
175
+ set_up_namespace_thread
176
+ end
177
+ end
178
+ assert_operator(@stats[:namespace_watch_failures], :>=, 3)
179
+ assert_operator(Thread.current[:namespace_watch_retry_count], :<=, 1)
180
+ assert_operator(Thread.current[:namespace_watch_retry_backoff_interval], :<=, 1)
181
+ end
182
+ end
183
+ end
184
+
185
+ test 'namespace watch resets watch retry count when error is received and connection to k8s API server is re-established' do
186
+ @client.stub :get_namespaces, @initial do
187
+ @client.stub :watch_namespaces, [@error] do
188
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
189
+ # no unrecoverable error was thrown during this period of time.
190
+ assert_raise Timeout::Error.new('execution expired') do
191
+ Timeout.timeout(3) do
192
+ set_up_namespace_thread
193
+ end
194
+ end
195
+ assert_operator(@stats[:namespace_watch_failures], :>=, 3)
196
+ assert_operator(Thread.current[:namespace_watch_retry_count], :<=, 1)
197
+ assert_operator(Thread.current[:namespace_watch_retry_backoff_interval], :<=, 1)
198
+ end
199
+ end
200
+ end
201
+
202
+ test 'namespace watch continues after retries succeed' do
203
+ @client.stub :get_namespaces, @initial do
204
+ @client.stub :watch_namespaces, [@modified, @error, @modified] do
205
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
206
+ # no unrecoverable error was thrown during this period of time.
207
+ assert_raise Timeout::Error.new('execution expired') do
208
+ Timeout.timeout(3) do
209
+ set_up_namespace_thread
210
+ end
211
+ end
212
+ assert_operator(@stats[:namespace_watch_failures], :>=, 3)
213
+ assert_operator(Thread.current[:namespace_watch_retry_count], :<=, 1)
214
+ assert_operator(Thread.current[:namespace_watch_retry_backoff_interval], :<=, 1)
215
+ assert_operator(@stats[:namespace_watch_error_type_notices], :>=, 3)
216
+ end
217
+ end
218
+ end
219
+
220
+ test 'namespace watch raises a GoneError when a 410 Gone error is received' do
221
+ @cache['gone_uid'] = {}
222
+ @client.stub :watch_namespaces, [@gone] do
223
+ assert_raise KubernetesMetadata::Common::GoneError do
224
+ process_namespace_watcher_notices(start_namespace_watch)
225
+ end
226
+ assert_equal(1, @stats[:namespace_watch_gone_notices])
227
+ end
228
+ end
229
+
230
+ test 'namespace watch retries when 410 Gone errors are encountered' do
231
+ @client.stub :get_namespaces, @initial do
232
+ @client.stub :watch_namespaces, [@created, @gone, @modified] do
233
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
234
+ # no unrecoverable error was thrown during this period of time.
235
+ assert_raise Timeout::Error.new('execution expired') do
236
+ Timeout.timeout(3) do
237
+ set_up_namespace_thread
238
+ end
239
+ end
240
+ assert_operator(@stats[:namespace_watch_gone_errors], :>=, 3)
241
+ assert_operator(@stats[:namespace_watch_gone_notices], :>=, 3)
242
+ end
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,344 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Fluentd Kubernetes Metadata Filter Plugin - Enrich Fluentd events with
5
+ # Kubernetes metadata
6
+ #
7
+ # Copyright 2015 Red Hat, Inc.
8
+ #
9
+ # Licensed under the Apache License, Version 2.0 (the "License");
10
+ # you may not use this file except in compliance with the License.
11
+ # You may obtain a copy of the License at
12
+ #
13
+ # http://www.apache.org/licenses/LICENSE-2.0
14
+ #
15
+ # Unless required by applicable law or agreed to in writing, software
16
+ # distributed under the License is distributed on an "AS IS" BASIS,
17
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
+ # See the License for the specific language governing permissions and
19
+ # limitations under the License.
20
+ #
21
+ require_relative '../helper'
22
+ require_relative 'watch_test'
23
+
24
+ class DefaultPodWatchStrategyTest < WatchTest
25
+ include KubernetesMetadata::WatchPods
26
+
27
+ setup do
28
+ @initial = {
29
+ kind: 'PodList',
30
+ metadata: { resourceVersion: '123' },
31
+ items: [
32
+ {
33
+ metadata: {
34
+ name: 'initial',
35
+ namespace: 'initial_ns',
36
+ uid: 'initial_uid',
37
+ labels: {}
38
+ },
39
+ spec: {
40
+ nodeName: 'aNodeName',
41
+ containers: [{
42
+ name: 'foo',
43
+ image: 'bar'
44
+ }, {
45
+ name: 'bar',
46
+ image: 'foo'
47
+ }]
48
+ },
49
+ status: {
50
+ podIP: '172.17.0.8'
51
+ }
52
+ },
53
+ {
54
+ metadata: {
55
+ name: 'modified',
56
+ namespace: 'create',
57
+ uid: 'modified_uid',
58
+ labels: {}
59
+ },
60
+ spec: {
61
+ nodeName: 'aNodeName',
62
+ containers: [{
63
+ name: 'foo',
64
+ image: 'bar'
65
+ }, {
66
+ name: 'bar',
67
+ image: 'foo'
68
+ }]
69
+ },
70
+ status: {
71
+ podIP: '172.17.0.8'
72
+ }
73
+ }
74
+ ]
75
+ }
76
+ @created = {
77
+ type: 'CREATED',
78
+ object: {
79
+ metadata: {
80
+ name: 'created',
81
+ namespace: 'create',
82
+ uid: 'created_uid',
83
+ resourceVersion: '122',
84
+ labels: {}
85
+ },
86
+ spec: {
87
+ nodeName: 'aNodeName',
88
+ containers: [{
89
+ name: 'foo',
90
+ image: 'bar'
91
+ }, {
92
+ name: 'bar',
93
+ image: 'foo'
94
+ }]
95
+ },
96
+ status: {
97
+ podIP: '172.17.0.8'
98
+ }
99
+ }
100
+ }
101
+ @modified = {
102
+ type: 'MODIFIED',
103
+ object: {
104
+ metadata: {
105
+ name: 'foo',
106
+ namespace: 'modified',
107
+ uid: 'modified_uid',
108
+ resourceVersion: '123',
109
+ labels: {}
110
+ },
111
+ spec: {
112
+ nodeName: 'aNodeName',
113
+ containers: [{
114
+ name: 'foo',
115
+ image: 'bar'
116
+ }, {
117
+ name: 'bar',
118
+ image: 'foo'
119
+ }]
120
+ },
121
+ status: {
122
+ podIP: '172.17.0.8',
123
+ containerStatuses: [
124
+ {
125
+ name: 'fabric8-console-container',
126
+ state: {
127
+ running: {
128
+ startedAt: '2015-05-08T09:22:44Z'
129
+ }
130
+ },
131
+ lastState: {},
132
+ ready: true,
133
+ restartCount: 0,
134
+ image: 'fabric8/hawtio-kubernetes:latest',
135
+ imageID: 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
136
+ containerID: 'docker://49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
137
+ }
138
+ ]
139
+ }
140
+ }
141
+ }
142
+ @deleted = {
143
+ type: 'DELETED',
144
+ object: {
145
+ metadata: {
146
+ name: 'deleteme',
147
+ namespace: 'deleted',
148
+ uid: 'deleted_uid',
149
+ resourceVersion: '124'
150
+ }
151
+ }
152
+ }
153
+ @error = {
154
+ type: 'ERROR',
155
+ object: {
156
+ message: 'some error message'
157
+ }
158
+ }
159
+ @gone = {
160
+ type: 'ERROR',
161
+ object: {
162
+ code: 410,
163
+ kind: 'Status',
164
+ message: 'too old resource version: 123 (391079)',
165
+ metadata: {
166
+ name: 'gone',
167
+ namespace: 'gone',
168
+ uid: 'gone_uid'
169
+ },
170
+ reason: 'Gone'
171
+ }
172
+ }
173
+ end
174
+
175
+ test 'pod list caches pods' do
176
+ orig_env_val = ENV['K8S_NODE_NAME']
177
+ ENV['K8S_NODE_NAME'] = 'aNodeName'
178
+ @client.stub :get_pods, @initial do
179
+ process_pod_watcher_notices(start_pod_watch)
180
+ assert_equal(true, @cache.key?('initial_uid'))
181
+ assert_equal(true, @cache.key?('modified_uid'))
182
+ assert_equal(2, @stats[:pod_cache_host_updates])
183
+ end
184
+ ENV['K8S_NODE_NAME'] = orig_env_val
185
+ end
186
+
187
+ test 'pod list caches pods and watch updates' do
188
+ orig_env_val = ENV['K8S_NODE_NAME']
189
+ ENV['K8S_NODE_NAME'] = 'aNodeName'
190
+ @client.stub :get_pods, @initial do
191
+ @client.stub :watch_pods, [@modified] do
192
+ process_pod_watcher_notices(start_pod_watch)
193
+ assert_equal(2, @stats[:pod_cache_host_updates])
194
+ assert_equal(1, @stats[:pod_cache_watch_updates])
195
+ end
196
+ end
197
+ ENV['K8S_NODE_NAME'] = orig_env_val
198
+ assert_equal('123', @last_seen_resource_version) # from @modified
199
+ end
200
+
201
+ test 'pod watch notice ignores CREATED' do
202
+ @client.stub :get_pods, @initial do
203
+ @client.stub :watch_pods, [@created] do
204
+ process_pod_watcher_notices(start_pod_watch)
205
+ assert_equal(false, @cache.key?('created_uid'))
206
+ assert_equal(1, @stats[:pod_cache_watch_ignored])
207
+ end
208
+ end
209
+ end
210
+
211
+ test 'pod watch notice is ignored when info not cached and MODIFIED is received' do
212
+ @client.stub :watch_pods, [@modified] do
213
+ process_pod_watcher_notices(start_pod_watch)
214
+ assert_equal(false, @cache.key?('modified_uid'))
215
+ assert_equal(1, @stats[:pod_cache_watch_misses])
216
+ end
217
+ end
218
+
219
+ test 'pod MODIFIED cached when hostname matches' do
220
+ orig_env_val = ENV['K8S_NODE_NAME']
221
+ ENV['K8S_NODE_NAME'] = 'aNodeName'
222
+ @client.stub :watch_pods, [@modified] do
223
+ process_pod_watcher_notices(start_pod_watch)
224
+ assert_equal(true, @cache.key?('modified_uid'))
225
+ assert_equal(1, @stats[:pod_cache_host_updates])
226
+ end
227
+ ENV['K8S_NODE_NAME'] = orig_env_val
228
+ end
229
+
230
+ test 'pod watch notice is updated when MODIFIED is received' do
231
+ @cache['modified_uid'] = {}
232
+ @client.stub :watch_pods, [@modified] do
233
+ process_pod_watcher_notices(start_pod_watch)
234
+ assert_equal(true, @cache.key?('modified_uid'))
235
+ assert_equal(1, @stats[:pod_cache_watch_updates])
236
+ end
237
+ end
238
+
239
+ test 'pod watch notice is ignored when delete is received' do
240
+ @cache['deleted_uid'] = {}
241
+ @client.stub :watch_pods, [@deleted] do
242
+ process_pod_watcher_notices(start_pod_watch)
243
+ assert_equal(true, @cache.key?('deleted_uid'))
244
+ assert_equal(1, @stats[:pod_cache_watch_delete_ignored])
245
+ end
246
+ end
247
+
248
+ test 'pod watch raises Fluent::UnrecoverableError when cannot re-establish connection to k8s API server' do
249
+ # Stub start_pod_watch to simulate initial successful connection to API server
250
+ stub(self).start_pod_watch
251
+ # Stub watch_pods to simluate not being able to set up watch connection to API server
252
+ stub(@client).watch_pods { raise }
253
+ @client.stub :get_pods, @initial do
254
+ assert_raise Fluent::UnrecoverableError do
255
+ set_up_pod_thread
256
+ end
257
+ end
258
+ assert_equal(3, @stats[:pod_watch_failures])
259
+ assert_equal(2, Thread.current[:pod_watch_retry_count])
260
+ assert_equal(4, Thread.current[:pod_watch_retry_backoff_interval])
261
+ assert_nil(@stats[:pod_watch_error_type_notices])
262
+ end
263
+
264
+ test 'pod watch resets watch retry count when exceptions are encountered and connection to k8s API server is re-established' do
265
+ @client.stub :get_pods, @initial do
266
+ @client.stub :watch_pods, [[@created, @exception_raised]] do
267
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
268
+ # no unrecoverable error was thrown during this period of time.
269
+ assert_raise Timeout::Error.new('execution expired') do
270
+ Timeout.timeout(3) do
271
+ set_up_pod_thread
272
+ end
273
+ end
274
+ assert_operator(@stats[:pod_watch_failures], :>=, 3)
275
+ assert_operator(Thread.current[:pod_watch_retry_count], :<=, 1)
276
+ assert_operator(Thread.current[:pod_watch_retry_backoff_interval], :<=, 1)
277
+ end
278
+ end
279
+ end
280
+
281
+ test 'pod watch resets watch retry count when error is received and connection to k8s API server is re-established' do
282
+ @client.stub :get_pods, @initial do
283
+ @client.stub :watch_pods, [@error] do
284
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
285
+ # no unrecoverable error was thrown during this period of time.
286
+ assert_raise Timeout::Error.new('execution expired') do
287
+ Timeout.timeout(3) do
288
+ set_up_pod_thread
289
+ end
290
+ end
291
+ assert_operator(@stats[:pod_watch_failures], :>=, 3)
292
+ assert_operator(Thread.current[:pod_watch_retry_count], :<=, 1)
293
+ assert_operator(Thread.current[:pod_watch_retry_backoff_interval], :<=, 1)
294
+ assert_operator(@stats[:pod_watch_error_type_notices], :>=, 3)
295
+ end
296
+ end
297
+ end
298
+
299
+ test 'pod watch continues after retries succeed' do
300
+ @client.stub :get_pods, @initial do
301
+ @client.stub :watch_pods, [@modified, @error, @modified] do
302
+ # Force the infinite watch loop to exit after 3 seconds. Verifies that
303
+ # no unrecoverable error was thrown during this period of time.
304
+ assert_raise Timeout::Error.new('execution expired') do
305
+ Timeout.timeout(3) do
306
+ set_up_pod_thread
307
+ end
308
+ end
309
+ assert_operator(@stats[:pod_watch_failures], :>=, 3)
310
+ assert_operator(Thread.current[:pod_watch_retry_count], :<=, 1)
311
+ assert_operator(Thread.current[:pod_watch_retry_backoff_interval], :<=, 1)
312
+ assert_operator(@stats[:pod_watch_error_type_notices], :>=, 3)
313
+ end
314
+ end
315
+ end
316
+
317
+ test 'pod watch raises a GoneError when a 410 Gone error is received' do
318
+ @cache['gone_uid'] = {}
319
+ @client.stub :watch_pods, [@gone] do
320
+ @last_seen_resource_version = '100'
321
+ assert_raise KubernetesMetadata::Common::GoneError do
322
+ process_pod_watcher_notices(start_pod_watch)
323
+ end
324
+ assert_equal(1, @stats[:pod_watch_gone_notices])
325
+ assert_nil @last_seen_resource_version # forced restart
326
+ end
327
+ end
328
+
329
+ test 'pod watch retries when 410 Gone errors are encountered' do
330
+ @client.stub :get_pods, @initial do
331
+ @client.stub :watch_pods, [@created, @gone, @modified] do
332
+ # Force the infinite watch loop to exit after 3 seconds because the code sleeps 3 times.
333
+ # Verifies that no unrecoverable error was thrown during this period of time.
334
+ assert_raise Timeout::Error.new('execution expired') do
335
+ Timeout.timeout(3) do
336
+ set_up_pod_thread
337
+ end
338
+ end
339
+ assert_operator(@stats[:pod_watch_gone_errors], :>=, 3)
340
+ assert_operator(@stats[:pod_watch_gone_notices], :>=, 3)
341
+ end
342
+ end
343
+ end
344
+ end