fluent-plugin-kubernetes_metadata_filter 2.5.0 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +6 -6
- data/Gemfile.lock +46 -40
- data/fluent-plugin-kubernetes_metadata_filter.gemspec +3 -6
- data/lib/fluent/plugin/filter_kubernetes_metadata.rb +38 -59
- data/lib/fluent/plugin/kubernetes_metadata_common.rb +26 -46
- data/lib/fluent/plugin/kubernetes_metadata_watch_namespaces.rb +29 -13
- data/lib/fluent/plugin/kubernetes_metadata_watch_pods.rb +46 -17
- data/test/helper.rb +17 -1
- data/test/plugin/test_cache_stats.rb +2 -5
- data/test/plugin/test_cache_strategy.rb +5 -8
- data/test/plugin/test_filter_kubernetes_metadata.rb +11 -10
- data/test/plugin/test_watch_namespaces.rb +116 -54
- data/test/plugin/test_watch_pods.rb +188 -119
- data/test/plugin/watch_test.rb +3 -10
- metadata +7 -28
@@ -16,6 +16,7 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
|
+
# TODO: this is mostly copy-paste from kubernetes_metadata_watch_pods.rb unify them
|
19
20
|
require_relative 'kubernetes_metadata_common'
|
20
21
|
|
21
22
|
module KubernetesMetadata
|
@@ -39,7 +40,13 @@ module KubernetesMetadata
|
|
39
40
|
begin
|
40
41
|
namespace_watcher ||= get_namespaces_and_start_watcher
|
41
42
|
process_namespace_watcher_notices(namespace_watcher)
|
42
|
-
rescue
|
43
|
+
rescue GoneError => e
|
44
|
+
# Expected error. Quietly go back through the loop in order to
|
45
|
+
# start watching from the latest resource versions
|
46
|
+
@stats.bump(:namespace_watch_gone_errors)
|
47
|
+
log.info("410 Gone encountered. Restarting namespace watch to reset resource versions.", e)
|
48
|
+
namespace_watcher = nil
|
49
|
+
rescue => e
|
43
50
|
@stats.bump(:namespace_watch_failures)
|
44
51
|
if Thread.current[:namespace_watch_retry_count] < @watch_retry_max_times
|
45
52
|
# Instead of raising exceptions and crashing Fluentd, swallow
|
@@ -68,8 +75,8 @@ module KubernetesMetadata
|
|
68
75
|
end
|
69
76
|
|
70
77
|
def start_namespace_watch
|
71
|
-
|
72
|
-
rescue
|
78
|
+
get_namespaces_and_start_watcher
|
79
|
+
rescue => e
|
73
80
|
message = "start_namespace_watch: Exception encountered setting up " \
|
74
81
|
"namespace watch from Kubernetes API #{@apiVersion} endpoint " \
|
75
82
|
"#{@kubernetes_url}: #{e.message}"
|
@@ -83,16 +90,20 @@ module KubernetesMetadata
|
|
83
90
|
# starting from that resourceVersion.
|
84
91
|
def get_namespaces_and_start_watcher
|
85
92
|
options = {
|
86
|
-
resource_version: '0' # Fetch from API server
|
93
|
+
resource_version: '0' # Fetch from API server cache instead of etcd quorum read
|
87
94
|
}
|
88
95
|
namespaces = @client.get_namespaces(options)
|
89
|
-
namespaces.each do |namespace|
|
90
|
-
cache_key = namespace
|
96
|
+
namespaces[:items].each do |namespace|
|
97
|
+
cache_key = namespace[:metadata][:uid]
|
91
98
|
@namespace_cache[cache_key] = parse_namespace_metadata(namespace)
|
92
99
|
@stats.bump(:namespace_cache_host_updates)
|
93
100
|
end
|
94
|
-
|
101
|
+
|
102
|
+
# continue watching from most recent resourceVersion
|
103
|
+
options[:resource_version] = namespaces[:metadata][:resourceVersion]
|
104
|
+
|
95
105
|
watcher = @client.watch_namespaces(options)
|
106
|
+
reset_namespace_watch_retry_stats
|
96
107
|
watcher
|
97
108
|
end
|
98
109
|
|
@@ -106,13 +117,13 @@ module KubernetesMetadata
|
|
106
117
|
# Process a watcher notice and potentially raise an exception.
|
107
118
|
def process_namespace_watcher_notices(watcher)
|
108
119
|
watcher.each do |notice|
|
109
|
-
case notice
|
120
|
+
case notice[:type]
|
110
121
|
when 'MODIFIED'
|
111
122
|
reset_namespace_watch_retry_stats
|
112
|
-
cache_key = notice
|
123
|
+
cache_key = notice[:object][:metadata][:uid]
|
113
124
|
cached = @namespace_cache[cache_key]
|
114
125
|
if cached
|
115
|
-
@namespace_cache[cache_key] = parse_namespace_metadata(notice
|
126
|
+
@namespace_cache[cache_key] = parse_namespace_metadata(notice[:object])
|
116
127
|
@stats.bump(:namespace_cache_watch_updates)
|
117
128
|
else
|
118
129
|
@stats.bump(:namespace_cache_watch_misses)
|
@@ -123,9 +134,14 @@ module KubernetesMetadata
|
|
123
134
|
# deleted but still processing logs
|
124
135
|
@stats.bump(:namespace_cache_watch_deletes_ignored)
|
125
136
|
when 'ERROR'
|
126
|
-
|
127
|
-
|
128
|
-
|
137
|
+
if notice[:object] && notice[:object][:code] == 410
|
138
|
+
@stats.bump(:namespace_watch_gone_notices)
|
139
|
+
raise GoneError
|
140
|
+
else
|
141
|
+
@stats.bump(:namespace_watch_error_type_notices)
|
142
|
+
message = notice[:object][:message] if notice[:object] && notice[:object][:message]
|
143
|
+
raise "Error while watching namespaces: #{message}"
|
144
|
+
end
|
129
145
|
else
|
130
146
|
reset_namespace_watch_retry_stats
|
131
147
|
# Don't pay attention to creations, since the created namespace may not
|
@@ -16,9 +16,11 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
|
+
# TODO: this is mostly copy-paste from kubernetes_metadata_watch_namespaces.rb unify them
|
19
20
|
require_relative 'kubernetes_metadata_common'
|
20
21
|
|
21
22
|
module KubernetesMetadata
|
23
|
+
|
22
24
|
module WatchPods
|
23
25
|
|
24
26
|
include ::KubernetesMetadata::Common
|
@@ -28,6 +30,7 @@ module KubernetesMetadata
|
|
28
30
|
# Fluent:ConfigError, so that users can inspect potential errors in
|
29
31
|
# the configuration.
|
30
32
|
pod_watcher = start_pod_watch
|
33
|
+
|
31
34
|
Thread.current[:pod_watch_retry_backoff_interval] = @watch_retry_interval
|
32
35
|
Thread.current[:pod_watch_retry_count] = 0
|
33
36
|
|
@@ -39,7 +42,13 @@ module KubernetesMetadata
|
|
39
42
|
begin
|
40
43
|
pod_watcher ||= get_pods_and_start_watcher
|
41
44
|
process_pod_watcher_notices(pod_watcher)
|
42
|
-
rescue
|
45
|
+
rescue GoneError => e
|
46
|
+
# Expected error. Quietly go back through the loop in order to
|
47
|
+
# start watching from the latest resource versions
|
48
|
+
@stats.bump(:pod_watch_gone_errors)
|
49
|
+
log.info("410 Gone encountered. Restarting pod watch to reset resource versions.", e)
|
50
|
+
pod_watcher = nil
|
51
|
+
rescue => e
|
43
52
|
@stats.bump(:pod_watch_failures)
|
44
53
|
if Thread.current[:pod_watch_retry_count] < @watch_retry_max_times
|
45
54
|
# Instead of raising exceptions and crashing Fluentd, swallow
|
@@ -69,7 +78,7 @@ module KubernetesMetadata
|
|
69
78
|
|
70
79
|
def start_pod_watch
|
71
80
|
get_pods_and_start_watcher
|
72
|
-
rescue
|
81
|
+
rescue => e
|
73
82
|
message = "start_pod_watch: Exception encountered setting up pod watch " \
|
74
83
|
"from Kubernetes API #{@apiVersion} endpoint " \
|
75
84
|
"#{@kubernetes_url}: #{e.message}"
|
@@ -83,19 +92,27 @@ module KubernetesMetadata
|
|
83
92
|
# from that resourceVersion.
|
84
93
|
def get_pods_and_start_watcher
|
85
94
|
options = {
|
86
|
-
resource_version: '0' # Fetch from API server
|
95
|
+
resource_version: '0' # Fetch from API server cache instead of etcd quorum read
|
87
96
|
}
|
88
97
|
if ENV['K8S_NODE_NAME']
|
89
98
|
options[:field_selector] = 'spec.nodeName=' + ENV['K8S_NODE_NAME']
|
90
99
|
end
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
100
|
+
if @last_seen_resource_version
|
101
|
+
options[:resource_version] = @last_seen_resource_version
|
102
|
+
else
|
103
|
+
pods = @client.get_pods(options)
|
104
|
+
pods[:items].each do |pod|
|
105
|
+
cache_key = pod[:metadata][:uid]
|
106
|
+
@cache[cache_key] = parse_pod_metadata(pod)
|
107
|
+
@stats.bump(:pod_cache_host_updates)
|
108
|
+
end
|
109
|
+
|
110
|
+
# continue watching from most recent resourceVersion
|
111
|
+
options[:resource_version] = pods[:metadata][:resourceVersion]
|
96
112
|
end
|
97
|
-
|
113
|
+
|
98
114
|
watcher = @client.watch_pods(options)
|
115
|
+
reset_pod_watch_retry_stats
|
99
116
|
watcher
|
100
117
|
end
|
101
118
|
|
@@ -109,16 +126,22 @@ module KubernetesMetadata
|
|
109
126
|
# Process a watcher notice and potentially raise an exception.
|
110
127
|
def process_pod_watcher_notices(watcher)
|
111
128
|
watcher.each do |notice|
|
112
|
-
|
129
|
+
# store version we processed to not reprocess it ... do not unset when there is no version in response
|
130
|
+
version = ( # TODO: replace with &.dig once we are on ruby 2.5+
|
131
|
+
notice[:object] && notice[:object][:metadata] && notice[:object][:metadata][:resourceVersion]
|
132
|
+
)
|
133
|
+
@last_seen_resource_version = version if version
|
134
|
+
|
135
|
+
case notice[:type]
|
113
136
|
when 'MODIFIED'
|
114
137
|
reset_pod_watch_retry_stats
|
115
|
-
cache_key = notice.object
|
138
|
+
cache_key = notice.dig(:object, :metadata, :uid)
|
116
139
|
cached = @cache[cache_key]
|
117
140
|
if cached
|
118
|
-
@cache[cache_key] = parse_pod_metadata(notice
|
141
|
+
@cache[cache_key] = parse_pod_metadata(notice[:object])
|
119
142
|
@stats.bump(:pod_cache_watch_updates)
|
120
|
-
elsif ENV['K8S_NODE_NAME'] == notice
|
121
|
-
@cache[cache_key] = parse_pod_metadata(notice
|
143
|
+
elsif ENV['K8S_NODE_NAME'] == notice[:object][:spec][:nodeName] then
|
144
|
+
@cache[cache_key] = parse_pod_metadata(notice[:object])
|
122
145
|
@stats.bump(:pod_cache_host_updates)
|
123
146
|
else
|
124
147
|
@stats.bump(:pod_cache_watch_misses)
|
@@ -129,9 +152,15 @@ module KubernetesMetadata
|
|
129
152
|
# deleted but still processing logs
|
130
153
|
@stats.bump(:pod_cache_watch_delete_ignored)
|
131
154
|
when 'ERROR'
|
132
|
-
|
133
|
-
|
134
|
-
|
155
|
+
if notice[:object] && notice[:object][:code] == 410
|
156
|
+
@last_seen_resource_version = nil # requested resourceVersion was too old, need to reset
|
157
|
+
@stats.bump(:pod_watch_gone_notices)
|
158
|
+
raise GoneError
|
159
|
+
else
|
160
|
+
@stats.bump(:pod_watch_error_type_notices)
|
161
|
+
message = notice[:object][:message] if notice[:object] && notice[:object][:message]
|
162
|
+
raise "Error while watching pods: #{message}"
|
163
|
+
end
|
135
164
|
else
|
136
165
|
reset_pod_watch_retry_stats
|
137
166
|
# Don't pay attention to creations, since the created pod may not
|
data/test/helper.rb
CHANGED
@@ -16,6 +16,7 @@
|
|
16
16
|
# See the License for the specific language governing permissions and
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
|
+
require 'bundler/setup'
|
19
20
|
require 'codeclimate-test-reporter'
|
20
21
|
SimpleCov.start do
|
21
22
|
formatter SimpleCov::Formatter::MultiFormatter.new [
|
@@ -31,8 +32,14 @@ require 'fileutils'
|
|
31
32
|
require 'fluent/log'
|
32
33
|
require 'fluent/test'
|
33
34
|
require 'minitest/autorun'
|
34
|
-
require 'webmock/test_unit'
|
35
35
|
require 'vcr'
|
36
|
+
require 'ostruct'
|
37
|
+
require 'fluent/plugin/filter_kubernetes_metadata'
|
38
|
+
require 'fluent/test/driver/filter'
|
39
|
+
require 'kubeclient'
|
40
|
+
|
41
|
+
require 'webmock/test_unit'
|
42
|
+
WebMock.disable_net_connect!
|
36
43
|
|
37
44
|
VCR.configure do |config|
|
38
45
|
config.cassette_library_dir = 'test/cassettes'
|
@@ -62,3 +69,12 @@ def ipv6_enabled?
|
|
62
69
|
false
|
63
70
|
end
|
64
71
|
end
|
72
|
+
|
73
|
+
# TEST_NAME='foo' ruby test_file.rb to run a single test case
|
74
|
+
if ENV["TEST_NAME"]
|
75
|
+
(class << Test::Unit::TestCase; self; end).prepend(Module.new do
|
76
|
+
def test(name)
|
77
|
+
super if name == ENV["TEST_NAME"]
|
78
|
+
end
|
79
|
+
end)
|
80
|
+
end
|
@@ -17,12 +17,9 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
19
|
require_relative '../helper'
|
20
|
-
require 'fluent/plugin/kubernetes_metadata_stats'
|
21
|
-
require 'webmock/test_unit'
|
22
|
-
WebMock.disable_net_connect!
|
23
20
|
|
24
21
|
class KubernetesMetadataCacheStatsTest < Test::Unit::TestCase
|
25
|
-
|
22
|
+
|
26
23
|
test 'watch stats' do
|
27
24
|
require 'lru_redux'
|
28
25
|
stats = KubernetesMetadata::Stats.new
|
@@ -32,5 +29,5 @@ class KubernetesMetadataCacheStatsTest < Test::Unit::TestCase
|
|
32
29
|
|
33
30
|
assert_equal("stats - deleted: 2, missed: 1", stats.to_s)
|
34
31
|
end
|
35
|
-
|
32
|
+
|
36
33
|
end
|
@@ -17,11 +17,6 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
19
|
require_relative '../helper'
|
20
|
-
require_relative '../../lib/fluent/plugin/kubernetes_metadata_cache_strategy'
|
21
|
-
require_relative '../../lib/fluent/plugin/kubernetes_metadata_stats'
|
22
|
-
require 'lru_redux'
|
23
|
-
require 'webmock/test_unit'
|
24
|
-
WebMock.disable_net_connect!
|
25
20
|
|
26
21
|
class TestCacheStrategy
|
27
22
|
include KubernetesMetadata::CacheStrategy
|
@@ -38,9 +33,11 @@ class TestCacheStrategy
|
|
38
33
|
attr_accessor :stats, :cache, :id_cache, :namespace_cache, :allow_orphans
|
39
34
|
|
40
35
|
def fetch_pod_metadata(namespace_name, pod_name)
|
36
|
+
{}
|
41
37
|
end
|
42
38
|
|
43
39
|
def fetch_namespace_metadata(namespace_name)
|
40
|
+
{}
|
44
41
|
end
|
45
42
|
|
46
43
|
def log
|
@@ -56,7 +53,7 @@ class TestCacheStrategy
|
|
56
53
|
end
|
57
54
|
|
58
55
|
class KubernetesMetadataCacheStrategyTest < Test::Unit::TestCase
|
59
|
-
|
56
|
+
|
60
57
|
def setup
|
61
58
|
@strategy = TestCacheStrategy.new
|
62
59
|
@cache_key = 'some_long_container_id'
|
@@ -114,7 +111,7 @@ class KubernetesMetadataCacheStrategyTest < Test::Unit::TestCase
|
|
114
111
|
# we ever will have and should allow us to process all the deleted
|
115
112
|
# pod records
|
116
113
|
exp = {
|
117
|
-
'pod_id'=> @cache_key,
|
114
|
+
'pod_id'=> @cache_key,
|
118
115
|
'namespace_id'=> @namespace_uuid
|
119
116
|
}
|
120
117
|
@strategy.stub :fetch_pod_metadata, {} do
|
@@ -175,7 +172,7 @@ class KubernetesMetadataCacheStrategyTest < Test::Unit::TestCase
|
|
175
172
|
end
|
176
173
|
assert_equal({}, @strategy.get_pod_metadata(@cache_key,'namespace', 'pod', @time, batch_miss_cache))
|
177
174
|
end
|
178
|
-
|
175
|
+
|
179
176
|
test 'when metadata is not cached and no metadata can be fetched and allowing orphans for multiple records' do
|
180
177
|
# we should never see this since pod meta should not be retrievable
|
181
178
|
# unless the namespace exists
|
@@ -17,11 +17,6 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
19
|
require_relative '../helper'
|
20
|
-
require 'fluent/test/driver/filter'
|
21
|
-
require 'fluent/plugin/filter_kubernetes_metadata'
|
22
|
-
|
23
|
-
require 'webmock/test_unit'
|
24
|
-
WebMock.disable_net_connect!
|
25
20
|
|
26
21
|
class KubernetesMetadataFilterTest < Test::Unit::TestCase
|
27
22
|
include Fluent
|
@@ -122,8 +117,8 @@ class KubernetesMetadataFilterTest < Test::Unit::TestCase
|
|
122
117
|
secret_dir #{dir}
|
123
118
|
")
|
124
119
|
assert_equal(d.instance.kubernetes_url, "https://localhost:8443/api")
|
125
|
-
|
126
|
-
|
120
|
+
assert_nil(d.instance.ca_file, nil)
|
121
|
+
assert_nil(d.instance.bearer_token_file)
|
127
122
|
}
|
128
123
|
ensure
|
129
124
|
ENV['KUBERNETES_SERVICE_HOST'] = nil
|
@@ -769,9 +764,13 @@ class KubernetesMetadataFilterTest < Test::Unit::TestCase
|
|
769
764
|
'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
|
770
765
|
'randomfield' => 'randomvalue'
|
771
766
|
}
|
772
|
-
VCR.use_cassettes([
|
773
|
-
|
774
|
-
|
767
|
+
VCR.use_cassettes([
|
768
|
+
{name: 'valid_kubernetes_api_server'},
|
769
|
+
{name: 'kubernetes_get_api_v1'},
|
770
|
+
{name: 'kubernetes_get_pod'},
|
771
|
+
{name: 'kubernetes_get_namespace_default'},
|
772
|
+
{name: 'metadata_from_tag_and_journald_fields'}
|
773
|
+
]) do
|
775
774
|
filtered = emit_with_tag(tag, msg, '
|
776
775
|
kubernetes_url https://localhost:8443
|
777
776
|
watch false
|
@@ -873,6 +872,7 @@ class KubernetesMetadataFilterTest < Test::Unit::TestCase
|
|
873
872
|
assert_equal(expected_kube_metadata, filtered[0])
|
874
873
|
end
|
875
874
|
end
|
875
|
+
|
876
876
|
test 'with CONTAINER_NAME that does not match' do
|
877
877
|
tag = 'var.log.containers.junk4_junk5_junk6-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
|
878
878
|
msg = {
|
@@ -897,6 +897,7 @@ class KubernetesMetadataFilterTest < Test::Unit::TestCase
|
|
897
897
|
assert_equal(expected_kube_metadata, filtered[0])
|
898
898
|
end
|
899
899
|
end
|
900
|
+
|
900
901
|
test 'with CONTAINER_NAME starts with k8s_ that does not match' do
|
901
902
|
tag = 'var.log.containers.junk4_junk5_junk6-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
|
902
903
|
msg = {
|
@@ -17,7 +17,6 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
#
|
19
19
|
require_relative '../helper'
|
20
|
-
require 'ostruct'
|
21
20
|
require_relative 'watch_test'
|
22
21
|
|
23
22
|
class WatchNamespacesTestTest < WatchTest
|
@@ -25,57 +24,72 @@ class WatchNamespacesTestTest < WatchTest
|
|
25
24
|
include KubernetesMetadata::WatchNamespaces
|
26
25
|
|
27
26
|
setup do
|
28
|
-
@initial =
|
29
|
-
'NamespaceList',
|
30
|
-
'123',
|
31
|
-
[
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
]
|
45
|
-
|
46
|
-
|
27
|
+
@initial = {
|
28
|
+
kind: 'NamespaceList',
|
29
|
+
metadata: {resourceVersion: '123'},
|
30
|
+
items: [
|
31
|
+
{
|
32
|
+
metadata: {
|
33
|
+
name: 'initial',
|
34
|
+
uid: 'initial_uid'
|
35
|
+
}
|
36
|
+
},
|
37
|
+
{
|
38
|
+
metadata: {
|
39
|
+
name: 'modified',
|
40
|
+
uid: 'modified_uid'
|
41
|
+
}
|
42
|
+
}
|
43
|
+
]
|
44
|
+
}
|
45
|
+
|
46
|
+
@created = {
|
47
47
|
type: 'CREATED',
|
48
48
|
object: {
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
49
|
+
metadata: {
|
50
|
+
name: 'created',
|
51
|
+
uid: 'created_uid'
|
52
|
+
}
|
53
53
|
}
|
54
|
-
|
55
|
-
@modified =
|
54
|
+
}
|
55
|
+
@modified = {
|
56
56
|
type: 'MODIFIED',
|
57
57
|
object: {
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
58
|
+
metadata: {
|
59
|
+
name: 'foo',
|
60
|
+
uid: 'modified_uid'
|
61
|
+
}
|
62
62
|
}
|
63
|
-
|
64
|
-
@deleted =
|
63
|
+
}
|
64
|
+
@deleted = {
|
65
65
|
type: 'DELETED',
|
66
66
|
object: {
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
metadata: {
|
68
|
+
name: 'deleteme',
|
69
|
+
uid: 'deleted_uid'
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
@error = {
|
74
|
+
type: 'ERROR',
|
75
|
+
object: {
|
76
|
+
message: 'some error message'
|
71
77
|
}
|
72
|
-
|
73
|
-
@
|
78
|
+
}
|
79
|
+
@gone = {
|
74
80
|
type: 'ERROR',
|
75
81
|
object: {
|
76
|
-
|
82
|
+
code: 410,
|
83
|
+
kind: 'Status',
|
84
|
+
message: 'too old resource version: 123 (391079)',
|
85
|
+
metadata: {
|
86
|
+
name: 'gone',
|
87
|
+
namespace: 'gone',
|
88
|
+
uid: 'gone_uid'
|
89
|
+
},
|
90
|
+
reason: 'Gone'
|
77
91
|
}
|
78
|
-
|
92
|
+
}
|
79
93
|
end
|
80
94
|
|
81
95
|
test 'namespace list caches namespaces' do
|
@@ -134,30 +148,52 @@ class WatchNamespacesTestTest < WatchTest
|
|
134
148
|
end
|
135
149
|
end
|
136
150
|
|
137
|
-
test 'namespace watch
|
151
|
+
test 'namespace watch raises Fluent::UnrecoverableError when cannot re-establish connection to k8s API server' do
|
152
|
+
# Stub start_namespace_watch to simulate initial successful connection to API server
|
153
|
+
stub(self).start_namespace_watch
|
154
|
+
# Stub watch_namespaces to simluate not being able to set up watch connection to API server
|
155
|
+
stub(@client).watch_namespaces { raise }
|
156
|
+
@client.stub :get_namespaces, @initial do
|
157
|
+
assert_raise Fluent::UnrecoverableError do
|
158
|
+
set_up_namespace_thread
|
159
|
+
end
|
160
|
+
end
|
161
|
+
assert_equal(3, @stats[:namespace_watch_failures])
|
162
|
+
assert_equal(2, Thread.current[:namespace_watch_retry_count])
|
163
|
+
assert_equal(4, Thread.current[:namespace_watch_retry_backoff_interval])
|
164
|
+
assert_nil(@stats[:namespace_watch_error_type_notices])
|
165
|
+
end
|
166
|
+
|
167
|
+
test 'namespace watch resets watch retry count when exceptions are encountered and connection to k8s API server is re-established' do
|
138
168
|
@client.stub :get_namespaces, @initial do
|
139
169
|
@client.stub :watch_namespaces, [[@created, @exception_raised]] do
|
140
|
-
|
141
|
-
|
170
|
+
# Force the infinite watch loop to exit after 3 seconds. Verifies that
|
171
|
+
# no unrecoverable error was thrown during this period of time.
|
172
|
+
assert_raise Timeout::Error.new('execution expired') do
|
173
|
+
Timeout.timeout(3) do
|
174
|
+
set_up_namespace_thread
|
175
|
+
end
|
142
176
|
end
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
assert_nil(@stats[:namespace_watch_error_type_notices])
|
177
|
+
assert_operator(@stats[:namespace_watch_failures], :>=, 3)
|
178
|
+
assert_operator(Thread.current[:namespace_watch_retry_count], :<=, 1)
|
179
|
+
assert_operator(Thread.current[:namespace_watch_retry_backoff_interval], :<=, 1)
|
147
180
|
end
|
148
181
|
end
|
149
182
|
end
|
150
183
|
|
151
|
-
test 'namespace watch
|
184
|
+
test 'namespace watch resets watch retry count when error is received and connection to k8s API server is re-established' do
|
152
185
|
@client.stub :get_namespaces, @initial do
|
153
186
|
@client.stub :watch_namespaces, [@error] do
|
154
|
-
|
155
|
-
|
187
|
+
# Force the infinite watch loop to exit after 3 seconds. Verifies that
|
188
|
+
# no unrecoverable error was thrown during this period of time.
|
189
|
+
assert_raise Timeout::Error.new('execution expired') do
|
190
|
+
Timeout.timeout(3) do
|
191
|
+
set_up_namespace_thread
|
192
|
+
end
|
156
193
|
end
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
assert_equal(3, @stats[:namespace_watch_error_type_notices])
|
194
|
+
assert_operator(@stats[:namespace_watch_failures], :>=, 3)
|
195
|
+
assert_operator(Thread.current[:namespace_watch_retry_count], :<=, 1)
|
196
|
+
assert_operator(Thread.current[:namespace_watch_retry_backoff_interval], :<=, 1)
|
161
197
|
end
|
162
198
|
end
|
163
199
|
end
|
@@ -179,4 +215,30 @@ class WatchNamespacesTestTest < WatchTest
|
|
179
215
|
end
|
180
216
|
end
|
181
217
|
end
|
218
|
+
|
219
|
+
test 'namespace watch raises a GoneError when a 410 Gone error is received' do
|
220
|
+
@cache['gone_uid'] = {}
|
221
|
+
@client.stub :watch_namespaces, [@gone] do
|
222
|
+
assert_raise KubernetesMetadata::Common::GoneError do
|
223
|
+
process_namespace_watcher_notices(start_namespace_watch)
|
224
|
+
end
|
225
|
+
assert_equal(1, @stats[:namespace_watch_gone_notices])
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
test 'namespace watch retries when 410 Gone errors are encountered' do
|
230
|
+
@client.stub :get_namespaces, @initial do
|
231
|
+
@client.stub :watch_namespaces, [@created, @gone, @modified] do
|
232
|
+
# Force the infinite watch loop to exit after 3 seconds. Verifies that
|
233
|
+
# no unrecoverable error was thrown during this period of time.
|
234
|
+
assert_raise Timeout::Error.new('execution expired') do
|
235
|
+
Timeout.timeout(3) do
|
236
|
+
set_up_namespace_thread
|
237
|
+
end
|
238
|
+
end
|
239
|
+
assert_operator(@stats[:namespace_watch_gone_errors], :>=, 3)
|
240
|
+
assert_operator(@stats[:namespace_watch_gone_notices], :>=, 3)
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
182
244
|
end
|