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 @@
1
+ 12345
@@ -0,0 +1,33 @@
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
+
23
+ class KubernetesMetadataCacheStatsTest < Test::Unit::TestCase
24
+ test 'watch stats' do
25
+ require 'lru_redux'
26
+ stats = KubernetesMetadata::Stats.new
27
+ stats.bump(:missed)
28
+ stats.bump(:deleted)
29
+ stats.bump(:deleted)
30
+
31
+ assert_equal('stats - deleted: 2, missed: 1', stats.to_s)
32
+ end
33
+ end
@@ -0,0 +1,194 @@
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
+
23
+ class TestCacheStrategy
24
+ include KubernetesMetadata::CacheStrategy
25
+
26
+ def initialize
27
+ @stats = KubernetesMetadata::Stats.new
28
+ @cache = LruRedux::TTL::ThreadSafeCache.new(100, 3600)
29
+ @id_cache = LruRedux::TTL::ThreadSafeCache.new(100, 3600)
30
+ @namespace_cache = LruRedux::TTL::ThreadSafeCache.new(100, 3600)
31
+ @orphaned_namespace_name = '.orphaned'
32
+ @orphaned_namespace_id = 'orphaned'
33
+ end
34
+
35
+ attr_accessor :stats, :cache, :id_cache, :namespace_cache, :allow_orphans
36
+
37
+ def fetch_pod_metadata(_namespace_name, _pod_name)
38
+ {}
39
+ end
40
+
41
+ def fetch_namespace_metadata(_namespace_name)
42
+ {}
43
+ end
44
+
45
+ def log
46
+ logger = {}
47
+ def logger.trace?
48
+ true
49
+ end
50
+
51
+ def logger.trace(message)
52
+ end
53
+ logger
54
+ end
55
+ end
56
+
57
+ class KubernetesMetadataCacheStrategyTest < Test::Unit::TestCase
58
+ def setup
59
+ @strategy = TestCacheStrategy.new
60
+ @cache_key = 'some_long_container_id'
61
+ @namespace_name = 'some_namespace_name'
62
+ @namespace_uuid = 'some_namespace_uuid'
63
+ @pod_name = 'some_pod_name'
64
+ @pod_uuid = 'some_pod_uuid'
65
+ @time = Time.now
66
+ @pod_meta = { 'pod_id' => @pod_uuid, 'labels' => { 'meta' => 'pod' } }
67
+ @namespace_meta = { 'namespace_id' => @namespace_uuid, 'creation_timestamp' => @time.to_s }
68
+ end
69
+
70
+ test 'when cached metadata is found' do
71
+ exp = @pod_meta.merge(@namespace_meta)
72
+ exp.delete('creation_timestamp')
73
+ @strategy.id_cache[@cache_key] = {
74
+ pod_id: @pod_uuid,
75
+ namespace_id: @namespace_uuid
76
+ }
77
+ @strategy.cache[@pod_uuid] = @pod_meta
78
+ @strategy.namespace_cache[@namespace_uuid] = @namespace_meta
79
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, {}))
80
+ end
81
+
82
+ test 'when previously processed record for pod but metadata is not cached and can not be fetched' do
83
+ exp = {
84
+ 'pod_id' => @pod_uuid,
85
+ 'namespace_id' => @namespace_uuid
86
+ }
87
+ @strategy.id_cache[@cache_key] = {
88
+ pod_id: @pod_uuid,
89
+ namespace_id: @namespace_uuid
90
+ }
91
+ @strategy.stub :fetch_pod_metadata, {} do
92
+ @strategy.stub :fetch_namespace_metadata, nil do
93
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, {}))
94
+ end
95
+ end
96
+ end
97
+
98
+ test 'when metadata is not cached and is fetched' do
99
+ exp = @pod_meta.merge(@namespace_meta)
100
+ exp.delete('creation_timestamp')
101
+ @strategy.stub :fetch_pod_metadata, @pod_meta do
102
+ @strategy.stub :fetch_namespace_metadata, @namespace_meta do
103
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, {}))
104
+ assert_true(@strategy.id_cache.key?(@cache_key))
105
+ end
106
+ end
107
+ end
108
+
109
+ test 'when metadata is not cached and pod is deleted and namespace metadata is fetched' do
110
+ # this is the case for a record from a deleted pod where no other
111
+ # records were read. using the container hash since that is all
112
+ # we ever will have and should allow us to process all the deleted
113
+ # pod records
114
+ exp = {
115
+ 'pod_id' => @cache_key,
116
+ 'namespace_id' => @namespace_uuid
117
+ }
118
+ @strategy.stub :fetch_pod_metadata, {} do
119
+ @strategy.stub :fetch_namespace_metadata, @namespace_meta do
120
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, {}))
121
+ assert_true(@strategy.id_cache.key?(@cache_key))
122
+ end
123
+ end
124
+ end
125
+
126
+ test 'when metadata is not cached and pod is deleted and namespace is for a different namespace with the same name' do
127
+ # this is the case for a record from a deleted pod from a deleted namespace
128
+ # where new namespace was created with the same name
129
+ exp = {
130
+ 'namespace_id' => @namespace_uuid
131
+ }
132
+ @strategy.stub :fetch_pod_metadata, {} do
133
+ @strategy.stub :fetch_namespace_metadata, @namespace_meta do
134
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time - 1 * 86_400, {}))
135
+ assert_true(@strategy.id_cache.key?(@cache_key))
136
+ end
137
+ end
138
+ end
139
+
140
+ test 'when metadata is not cached and no metadata can be fetched and not allowing orphans' do
141
+ # we should never see this since pod meta should not be retrievable
142
+ # unless the namespace exists
143
+ @strategy.stub :fetch_pod_metadata, @pod_meta do
144
+ @strategy.stub :fetch_namespace_metadata, {} do
145
+ assert_equal({}, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time - 1 * 86_400, {}))
146
+ end
147
+ end
148
+ end
149
+
150
+ test 'when metadata is not cached and no metadata can be fetched and allowing orphans' do
151
+ # we should never see this since pod meta should not be retrievable
152
+ # unless the namespace exists
153
+ @strategy.allow_orphans = true
154
+ exp = {
155
+ 'orphaned_namespace' => 'namespace',
156
+ 'namespace_name' => '.orphaned',
157
+ 'namespace_id' => 'orphaned'
158
+ }
159
+ @strategy.stub :fetch_pod_metadata, @pod_meta do
160
+ @strategy.stub :fetch_namespace_metadata, {} do
161
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time - 1 * 86_400, {}))
162
+ end
163
+ end
164
+ end
165
+
166
+ test 'when metadata is not cached and no metadata can be fetched and not allowing orphans for multiple records' do
167
+ # processing a batch of records with no meta. ideally we only hit the api server once
168
+ batch_miss_cache = {}
169
+ @strategy.stub :fetch_pod_metadata, {} do
170
+ @strategy.stub :fetch_namespace_metadata, {} do
171
+ assert_equal({}, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, batch_miss_cache))
172
+ end
173
+ end
174
+ assert_equal({}, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, batch_miss_cache))
175
+ end
176
+
177
+ test 'when metadata is not cached and no metadata can be fetched and allowing orphans for multiple records' do
178
+ # we should never see this since pod meta should not be retrievable
179
+ # unless the namespace exists
180
+ @strategy.allow_orphans = true
181
+ exp = {
182
+ 'orphaned_namespace' => 'namespace',
183
+ 'namespace_name' => '.orphaned',
184
+ 'namespace_id' => 'orphaned'
185
+ }
186
+ batch_miss_cache = {}
187
+ @strategy.stub :fetch_pod_metadata, {} do
188
+ @strategy.stub :fetch_namespace_metadata, {} do
189
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, batch_miss_cache))
190
+ end
191
+ end
192
+ assert_equal(exp, @strategy.get_pod_metadata(@cache_key, 'namespace', 'pod', @time, batch_miss_cache))
193
+ end
194
+ end
@@ -0,0 +1,1012 @@
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
+
23
+ class KubernetesMetadataFilterTest < Test::Unit::TestCase
24
+ include Fluent
25
+
26
+ setup do
27
+ Fluent::Test.setup
28
+ @time = Fluent::Engine.now
29
+ end
30
+
31
+ DEFAULT_TAG = 'var.log.containers.fabric8-console-controller-98rqc_default_fabric8-console-container-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459.log'
32
+
33
+ def create_driver(conf = '')
34
+ Test::Driver::Filter.new(Plugin::KubernetesMetadataFilter).configure(conf)
35
+ end
36
+
37
+ sub_test_case 'configure' do
38
+ test 'check default' do
39
+ d = create_driver
40
+ assert_equal(1000, d.instance.cache_size)
41
+ end
42
+
43
+ test 'kubernetes url' do
44
+ VCR.use_cassette('valid_kubernetes_api_server') do
45
+ d = create_driver('
46
+ kubernetes_url https://localhost:8443
47
+ watch false
48
+ ')
49
+ assert_equal('https://localhost:8443', d.instance.kubernetes_url)
50
+ assert_equal(1000, d.instance.cache_size)
51
+ end
52
+ end
53
+
54
+ test 'cache size' do
55
+ VCR.use_cassette('valid_kubernetes_api_server') do
56
+ d = create_driver('
57
+ kubernetes_url https://localhost:8443
58
+ watch false
59
+ cache_size 1
60
+ ')
61
+ assert_equal('https://localhost:8443', d.instance.kubernetes_url)
62
+ assert_equal(1, d.instance.cache_size)
63
+ end
64
+ end
65
+
66
+ test 'invalid API server config' do
67
+ VCR.use_cassette('invalid_api_server_config') do
68
+ assert_raise Fluent::ConfigError do
69
+ create_driver('
70
+ kubernetes_url https://localhost:8443
71
+ bearer_token_file test/plugin/test.token
72
+ watch false
73
+ verify_ssl false
74
+ ')
75
+ end
76
+ end
77
+ end
78
+
79
+ test 'service account credentials' do
80
+ VCR.use_cassette('valid_kubernetes_api_server') do
81
+ ENV['KUBERNETES_SERVICE_HOST'] = 'localhost'
82
+ ENV['KUBERNETES_SERVICE_PORT'] = '8443'
83
+
84
+ Dir.mktmpdir do |dir|
85
+ # Fake token file and CA crt.
86
+ expected_cert_path = File.join(dir, Plugin::KubernetesMetadataFilter::K8_POD_CA_CERT)
87
+ expected_token_path = File.join(dir, Plugin::KubernetesMetadataFilter::K8_POD_TOKEN)
88
+
89
+ File.open(expected_cert_path, 'w')
90
+ File.open(expected_token_path, 'w')
91
+
92
+ d = create_driver("
93
+ watch false
94
+ secret_dir #{dir}
95
+ ")
96
+
97
+ assert_equal(d.instance.kubernetes_url, 'https://localhost:8443/api')
98
+ assert_equal(d.instance.ca_file, expected_cert_path)
99
+ assert_equal(d.instance.bearer_token_file, expected_token_path)
100
+ end
101
+ ensure
102
+ ENV['KUBERNETES_SERVICE_HOST'] = nil
103
+ ENV['KUBERNETES_SERVICE_PORT'] = nil
104
+ end
105
+ end
106
+
107
+ test 'service account credential files are tested for existence' do
108
+ VCR.use_cassette('valid_kubernetes_api_server') do
109
+ ENV['KUBERNETES_SERVICE_HOST'] = 'localhost'
110
+ ENV['KUBERNETES_SERVICE_PORT'] = '8443'
111
+
112
+ Dir.mktmpdir do |dir|
113
+ d = create_driver("
114
+ watch false
115
+ secret_dir #{dir}
116
+ ")
117
+ assert_equal(d.instance.kubernetes_url, 'https://localhost:8443/api')
118
+ assert_nil(d.instance.ca_file, nil)
119
+ assert_nil(d.instance.bearer_token_file)
120
+ end
121
+ ensure
122
+ ENV['KUBERNETES_SERVICE_HOST'] = nil
123
+ ENV['KUBERNETES_SERVICE_PORT'] = nil
124
+ end
125
+ end
126
+ end
127
+
128
+ sub_test_case 'filter' do
129
+ def emit(msg = {}, config = '
130
+ kubernetes_url https://localhost:8443
131
+ watch false
132
+ cache_size 1
133
+ ', d: nil)
134
+ d = create_driver(config) if d.nil?
135
+ d.run(default_tag: DEFAULT_TAG) do
136
+ d.feed(@time, msg)
137
+ end
138
+ d.filtered.map(&:last)
139
+ end
140
+
141
+ def emit_with_tag(tag, msg = {}, config = '
142
+ kubernetes_url https://localhost:8443
143
+ watch false
144
+ cache_size 1
145
+ ')
146
+ d = create_driver(config)
147
+ d.run(default_tag: tag) do
148
+ d.feed(@time, msg)
149
+ end
150
+ d.filtered.map(&:last)
151
+ end
152
+
153
+ test 'nil event stream' do
154
+ # not certain how this is possible but adding test to properly
155
+ # guard against this condition we have seen - test for nil,
156
+ # empty, no empty method, not an event stream
157
+ plugin = create_driver.instance
158
+ plugin.filter_stream('tag', nil)
159
+ plugin.filter_stream('tag', Fluent::MultiEventStream.new)
160
+ end
161
+
162
+ test 'inability to connect to the api server handles exception and doensnt block pipeline' do
163
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }]) do
164
+ driver = create_driver('
165
+ kubernetes_url https://localhost:8443
166
+ watch false
167
+ cache_size 1
168
+ ')
169
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default/pods/fabric8-console-controller-98rqc').to_raise(SocketError.new('error from pod fetch'))
170
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default').to_raise(SocketError.new('socket error from namespace fetch'))
171
+ filtered = emit({ 'time' => '2015-05-08T09:22:01Z' }, '', d: driver)
172
+ expected_kube_metadata = {
173
+ 'time' => '2015-05-08T09:22:01Z',
174
+ 'docker' => {
175
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
176
+ },
177
+ 'kubernetes' => {
178
+ 'pod_name' => 'fabric8-console-controller-98rqc',
179
+ 'container_name' => 'fabric8-console-container',
180
+ 'namespace_id' => 'orphaned',
181
+ 'namespace_name' => '.orphaned',
182
+ 'orphaned_namespace' => 'default'
183
+ }
184
+ }
185
+ assert_equal(expected_kube_metadata, filtered[0])
186
+ end
187
+ end
188
+
189
+ test 'with docker & kubernetes metadata where id cache hit and metadata miss' do
190
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }]) do
191
+ driver = create_driver('
192
+ kubernetes_url https://localhost:8443
193
+ watch false
194
+ cache_size 1
195
+ ')
196
+ cache = driver.instance.instance_variable_get(:@id_cache)
197
+ cache['49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'] = {
198
+ pod_id: 'c76927af-f563-11e4-b32d-54ee7527188d',
199
+ namespace_id: '898268c8-4a36-11e5-9d81-42010af0194c'
200
+ }
201
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default/pods/fabric8-console-controller-98rqc').to_timeout
202
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default').to_timeout
203
+ filtered = emit({ 'time' => '2015-05-08T09:22:01Z' }, '', d: driver)
204
+ expected_kube_metadata = {
205
+ 'time' => '2015-05-08T09:22:01Z',
206
+ 'docker' => {
207
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
208
+ },
209
+ 'kubernetes' => {
210
+ 'pod_name' => 'fabric8-console-controller-98rqc',
211
+ 'container_name' => 'fabric8-console-container',
212
+ 'namespace_name' => 'default',
213
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
214
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d'
215
+ }
216
+ }
217
+
218
+ assert_equal(expected_kube_metadata, filtered[0])
219
+ end
220
+ end
221
+
222
+ test 'with docker & kubernetes metadata where id cache hit and metadata is reloaded' do
223
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' }, { name: 'kubernetes_get_namespace_default' }]) do
224
+ driver = create_driver('
225
+ kubernetes_url https://localhost:8443
226
+ watch false
227
+ cache_size 1
228
+ ')
229
+ cache = driver.instance.instance_variable_get(:@id_cache)
230
+ cache['49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'] = {
231
+ pod_id: 'c76927af-f563-11e4-b32d-54ee7527188d',
232
+ namespace_id: '898268c8-4a36-11e5-9d81-42010af0194c'
233
+ }
234
+ filtered = emit({ 'time' => '2015-05-08T09:22:01Z' }, '', d: driver)
235
+ expected_kube_metadata = {
236
+ 'time' => '2015-05-08T09:22:01Z',
237
+ 'docker' => {
238
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
239
+ },
240
+ 'kubernetes' => {
241
+ 'host' => 'jimmi-redhat.localnet',
242
+ 'pod_name' => 'fabric8-console-controller-98rqc',
243
+ 'container_name' => 'fabric8-console-container',
244
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
245
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
246
+ 'namespace_name' => 'default',
247
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
248
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
249
+ 'pod_ip' => '172.17.0.8',
250
+ 'master_url' => 'https://localhost:8443',
251
+ 'labels' => {
252
+ 'component' => 'fabric8Console'
253
+ }
254
+ }
255
+ }
256
+
257
+ assert_equal(expected_kube_metadata, filtered[0])
258
+ end
259
+ end
260
+
261
+ test 'with docker & kubernetes metadata' do
262
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' }, { name: 'kubernetes_get_namespace_default' }]) do
263
+ filtered = emit({ 'time' => '2015-05-08T09:22:01Z' })
264
+ expected_kube_metadata = {
265
+ 'time' => '2015-05-08T09:22:01Z',
266
+ 'docker' => {
267
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
268
+ },
269
+ 'kubernetes' => {
270
+ 'host' => 'jimmi-redhat.localnet',
271
+ 'pod_name' => 'fabric8-console-controller-98rqc',
272
+ 'container_name' => 'fabric8-console-container',
273
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
274
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
275
+ 'namespace_name' => 'default',
276
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
277
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
278
+ 'pod_ip' => '172.17.0.8',
279
+ 'master_url' => 'https://localhost:8443',
280
+ 'labels' => {
281
+ 'component' => 'fabric8Console'
282
+ }
283
+ }
284
+ }
285
+
286
+ assert_equal(expected_kube_metadata, filtered[0])
287
+ end
288
+ end
289
+
290
+ test 'with docker & kubernetes metadata & namespace_id enabled' do
291
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
292
+ { name: 'kubernetes_get_namespace_default', options: { allow_playback_repeats: true } }]) do
293
+ filtered = emit({}, '
294
+ kubernetes_url https://localhost:8443
295
+ watch false
296
+ cache_size 1
297
+ ')
298
+ expected_kube_metadata = {
299
+ 'docker' => {
300
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
301
+ },
302
+ 'kubernetes' => {
303
+ 'host' => 'jimmi-redhat.localnet',
304
+ 'pod_name' => 'fabric8-console-controller-98rqc',
305
+ 'container_name' => 'fabric8-console-container',
306
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
307
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
308
+ 'namespace_name' => 'default',
309
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
310
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
311
+ 'pod_ip' => '172.17.0.8',
312
+ 'master_url' => 'https://localhost:8443',
313
+ 'labels' => {
314
+ 'component' => 'fabric8Console'
315
+ }
316
+ }
317
+ }
318
+ assert_equal(expected_kube_metadata, filtered[0])
319
+ end
320
+ end
321
+
322
+ test 'with docker & kubernetes metadata using bearer token' do
323
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server_using_token' }, { name: 'kubernetes_get_api_v1_using_token' },
324
+ { name: 'kubernetes_get_pod_using_token' }, { name: 'kubernetes_get_namespace_default_using_token' }]) do
325
+ filtered = emit({}, '
326
+ kubernetes_url https://localhost:8443
327
+ verify_ssl false
328
+ watch false
329
+ bearer_token_file test/plugin/test.token
330
+ ')
331
+ expected_kube_metadata = {
332
+ 'docker' => {
333
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
334
+ },
335
+ 'kubernetes' => {
336
+ 'host' => 'jimmi-redhat.localnet',
337
+ 'pod_name' => 'fabric8-console-controller-98rqc',
338
+ 'container_name' => 'fabric8-console-container',
339
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
340
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
341
+ 'namespace_name' => 'default',
342
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
343
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
344
+ 'pod_ip' => '172.17.0.8',
345
+ 'master_url' => 'https://localhost:8443',
346
+ 'labels' => {
347
+ 'component' => 'fabric8Console'
348
+ }
349
+ }
350
+ }
351
+ assert_equal(expected_kube_metadata, filtered[0])
352
+ end
353
+ end
354
+
355
+ test 'with docker & kubernetes metadata but no configured api server' do
356
+ filtered = emit({}, '')
357
+ expected_kube_metadata = {
358
+ 'docker' => {
359
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
360
+ },
361
+ 'kubernetes' => {
362
+ 'pod_name' => 'fabric8-console-controller-98rqc',
363
+ 'container_name' => 'fabric8-console-container',
364
+ 'namespace_name' => 'default'
365
+ }
366
+ }
367
+ assert_equal(expected_kube_metadata, filtered[0])
368
+ end
369
+
370
+ test 'with docker & inaccessible kubernetes metadata' do
371
+ stub_request(:any, 'https://localhost:8443/api').to_return(
372
+ 'body' => {
373
+ 'versions' => ['v1beta3', 'v1']
374
+ }.to_json
375
+ )
376
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default/pods/fabric8-console-controller-98rqc').to_timeout
377
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default').to_timeout
378
+ filtered = emit
379
+ expected_kube_metadata = {
380
+ 'docker' => {
381
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
382
+ },
383
+ 'kubernetes' => {
384
+ 'pod_name' => 'fabric8-console-controller-98rqc',
385
+ 'container_name' => 'fabric8-console-container',
386
+ 'namespace_name' => '.orphaned',
387
+ 'orphaned_namespace' => 'default',
388
+ 'namespace_id' => 'orphaned'
389
+ }
390
+ }
391
+ assert_equal(expected_kube_metadata, filtered[0])
392
+ end
393
+
394
+ test 'with dot in pod name' do
395
+ stub_request(:any, 'https://localhost:8443/api').to_return(
396
+ 'body' => {
397
+ 'versions' => ['v1beta3', 'v1']
398
+ }.to_json
399
+ )
400
+ stub_request(:any, 'https://localhost:8443/api/v1/namespaces/default/pods/fabric8-console-controller.98rqc').to_timeout
401
+ filtered = emit_with_tag('var.log.containers.fabric8-console-controller.98rqc_default_fabric8-console-container-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459.log', {}, '')
402
+ expected_kube_metadata = {
403
+ 'docker' => {
404
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
405
+ },
406
+ 'kubernetes' => {
407
+ 'pod_name' => 'fabric8-console-controller.98rqc',
408
+ 'container_name' => 'fabric8-console-container',
409
+ 'namespace_name' => 'default'
410
+ }
411
+ }
412
+ assert_equal(expected_kube_metadata, filtered[0])
413
+ end
414
+
415
+ test 'with docker metadata, non-kubernetes' do
416
+ filtered = emit_with_tag('non-kubernetes', {}, '')
417
+ assert_false(filtered[0].key?(:kubernetes))
418
+ end
419
+
420
+ test 'ignores invalid json in log field' do
421
+ json_log = "{'foo':123}"
422
+ msg = {
423
+ 'log' => json_log
424
+ }
425
+ filtered = emit_with_tag('non-kubernetes', msg, '')
426
+ assert_equal(msg, filtered[0])
427
+ end
428
+
429
+ test 'with kubernetes dotted labels, de_dot enabled' do
430
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
431
+ { name: 'kubernetes_docker_metadata_dotted_labels' }]) do
432
+ filtered = emit({}, '
433
+ kubernetes_url https://localhost:8443
434
+ watch false
435
+ cache_size 1
436
+ ')
437
+ expected_kube_metadata = {
438
+ 'docker' => {
439
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
440
+ },
441
+ 'kubernetes' => {
442
+ 'host' => 'jimmi-redhat.localnet',
443
+ 'pod_name' => 'fabric8-console-controller-98rqc',
444
+ 'container_name' => 'fabric8-console-container',
445
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
446
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
447
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
448
+ 'namespace_labels' => {
449
+ 'kubernetes_io/namespacetest' => 'somevalue'
450
+ },
451
+ 'namespace_name' => 'default',
452
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
453
+ 'pod_ip' => '172.17.0.8',
454
+ 'master_url' => 'https://localhost:8443',
455
+ 'labels' => {
456
+ 'kubernetes_io/test' => 'somevalue'
457
+ }
458
+ }
459
+ }
460
+ assert_equal(expected_kube_metadata, filtered[0])
461
+ end
462
+ end
463
+
464
+ test 'with kubernetes dotted labels, de_dot disabled' do
465
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
466
+ { name: 'kubernetes_docker_metadata_dotted_labels' }]) do
467
+ filtered = emit({}, '
468
+ kubernetes_url https://localhost:8443
469
+ watch false
470
+ cache_size 1
471
+ de_dot false
472
+ ')
473
+ expected_kube_metadata = {
474
+ 'docker' => {
475
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
476
+ },
477
+ 'kubernetes' => {
478
+ 'host' => 'jimmi-redhat.localnet',
479
+ 'pod_name' => 'fabric8-console-controller-98rqc',
480
+ 'container_name' => 'fabric8-console-container',
481
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
482
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
483
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
484
+ 'namespace_labels' => {
485
+ 'kubernetes.io/namespacetest' => 'somevalue'
486
+ },
487
+ 'namespace_name' => 'default',
488
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
489
+ 'pod_ip' => '172.17.0.8',
490
+ 'master_url' => 'https://localhost:8443',
491
+ 'labels' => {
492
+ 'kubernetes.io/test' => 'somevalue'
493
+ }
494
+ }
495
+ }
496
+ assert_equal(expected_kube_metadata, filtered[0])
497
+ end
498
+ end
499
+
500
+ test 'invalid de_dot_separator config' do
501
+ assert_raise Fluent::ConfigError do
502
+ create_driver('
503
+ de_dot_separator contains.
504
+ ')
505
+ end
506
+ end
507
+
508
+ test 'with records from journald and docker & kubernetes metadata' do
509
+ # with use_journal true should ignore tags and use CONTAINER_NAME and CONTAINER_ID_FULL
510
+ tag = 'var.log.containers.junk1_junk2_junk3-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
511
+ msg = {
512
+ 'CONTAINER_NAME' => 'k8s_fabric8-console-container.db89db89_fabric8-console-controller-98rqc_default_c76927af-f563-11e4-b32d-54ee7527188d_89db89db',
513
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
514
+ 'randomfield' => 'randomvalue'
515
+ }
516
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
517
+ { name: 'kubernetes_get_namespace_default' }]) do
518
+ filtered = emit_with_tag(tag, msg, '
519
+ kubernetes_url https://localhost:8443
520
+ watch false
521
+ cache_size 1
522
+ use_journal true
523
+ ')
524
+ expected_kube_metadata = {
525
+ 'docker' => {
526
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
527
+ },
528
+ 'kubernetes' => {
529
+ 'host' => 'jimmi-redhat.localnet',
530
+ 'pod_name' => 'fabric8-console-controller-98rqc',
531
+ 'container_name' => 'fabric8-console-container',
532
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
533
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
534
+ 'namespace_name' => 'default',
535
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
536
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
537
+ 'pod_ip' => '172.17.0.8',
538
+ 'master_url' => 'https://localhost:8443',
539
+ 'labels' => {
540
+ 'component' => 'fabric8Console'
541
+ }
542
+ }
543
+ }.merge(msg)
544
+ assert_equal(expected_kube_metadata, filtered[0])
545
+ end
546
+ end
547
+
548
+ test 'with records from journald and docker & kubernetes metadata & namespace_id enabled' do
549
+ # with use_journal true should ignore tags and use CONTAINER_NAME and CONTAINER_ID_FULL
550
+ tag = 'var.log.containers.junk1_junk2_junk3-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
551
+ msg = {
552
+ 'CONTAINER_NAME' => 'k8s_fabric8-console-container.db89db89_fabric8-console-controller-98rqc_default_c76927af-f563-11e4-b32d-54ee7527188d_89db89db',
553
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
554
+ 'randomfield' => 'randomvalue'
555
+ }
556
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
557
+ { name: 'kubernetes_get_namespace_default', options: { allow_playback_repeats: true } }]) do
558
+ filtered = emit_with_tag(tag, msg, '
559
+ kubernetes_url https://localhost:8443
560
+ watch false
561
+ cache_size 1
562
+ use_journal true
563
+ ')
564
+ expected_kube_metadata = {
565
+ 'docker' => {
566
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
567
+ },
568
+ 'kubernetes' => {
569
+ 'host' => 'jimmi-redhat.localnet',
570
+ 'pod_name' => 'fabric8-console-controller-98rqc',
571
+ 'container_name' => 'fabric8-console-container',
572
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
573
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
574
+ 'namespace_name' => 'default',
575
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
576
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
577
+ 'pod_ip' => '172.17.0.8',
578
+ 'master_url' => 'https://localhost:8443',
579
+ 'labels' => {
580
+ 'component' => 'fabric8Console'
581
+ }
582
+ }
583
+ }.merge(msg)
584
+ assert_equal(expected_kube_metadata, filtered[0])
585
+ end
586
+ end
587
+
588
+ test 'with records from journald and docker & kubernetes metadata with use_journal unset' do
589
+ # with use_journal unset, should still use the journal fields instead of tag fields
590
+ tag = 'var.log.containers.fabric8-console-controller-98rqc_default_fabric8-console-container-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459.log'
591
+ msg = {
592
+ 'CONTAINER_NAME' => 'k8s_journald-container-name.db89db89_journald-pod-name_journald-namespace-name_c76927af-f563-11e4-b32d-54ee7527188d_89db89db',
593
+ 'CONTAINER_ID_FULL' => '838350c64bacba968d39a30a50789b2795291fceca6ccbff55298671d46b0e3b',
594
+ 'kubernetes' => {
595
+ 'namespace_name' => 'k8s-namespace-name',
596
+ 'pod_name' => 'k8s-pod-name',
597
+ 'container_name' => 'k8s-container-name'
598
+ },
599
+ 'docker' => { 'container_id' => 'e463bc0d3ae38f5c89d92dca49b30e049e899799920b79d4d5f705acbe82ba95' },
600
+ 'randomfield' => 'randomvalue'
601
+ }
602
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
603
+ { name: 'kubernetes_get_namespace_default' },
604
+ { name: 'metadata_from_tag_journald_and_kubernetes_fields' }]) do
605
+ es = emit_with_tag(tag, msg, '
606
+ kubernetes_url https://localhost:8443
607
+ watch false
608
+ cache_size 1
609
+ ')
610
+ expected_kube_metadata = {
611
+ 'docker' => {
612
+ 'container_id' => 'e463bc0d3ae38f5c89d92dca49b30e049e899799920b79d4d5f705acbe82ba95'
613
+ },
614
+ 'kubernetes' => {
615
+ 'host' => 'jimmi-redhat.localnet',
616
+ 'pod_name' => 'k8s-pod-name',
617
+ 'container_name' => 'k8s-container-name',
618
+ 'container_image' => 'k8s-container-image:latest',
619
+ 'container_image_id' => 'docker://d78c5217c41e9af08d37d9ae2cb070afa1fe3da6bc77bfb18879a8b4bfdf8a34',
620
+ 'namespace_name' => 'k8s-namespace-name',
621
+ 'namespace_id' => '8e0dc8fc-59f2-49f7-a3e2-ed0913e19d9f',
622
+ 'pod_id' => 'ebabf749-5fcd-4750-a3f0-aedd89476da8',
623
+ 'pod_ip' => '172.17.0.8',
624
+ 'master_url' => 'https://localhost:8443',
625
+ 'labels' => {
626
+ 'component' => 'k8s-test'
627
+ }
628
+ }
629
+ }.merge(msg) { |key, oldval, newval| (key == 'kubernetes') || (key == 'docker') ? oldval : newval }
630
+ assert_equal(expected_kube_metadata, es[0])
631
+ end
632
+ end
633
+
634
+ test 'with records from journald and docker & kubernetes metadata with lookup_from_k8s_field false' do
635
+ # with use_journal unset, should still use the journal fields instead of tag fields
636
+ tag = 'var.log.containers.fabric8-console-controller-98rqc_default_fabric8-console-container-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459.log'
637
+ msg = {
638
+ 'CONTAINER_NAME' => 'k8s_journald-container-name.db89db89_journald-pod-name_journald-namespace-name_c76927af-f563-11e4-b32d-54ee7527188d_89db89db',
639
+ 'CONTAINER_ID_FULL' => '838350c64bacba968d39a30a50789b2795291fceca6ccbff55298671d46b0e3b',
640
+ 'kubernetes' => {
641
+ 'namespace_name' => 'k8s-namespace-name',
642
+ 'pod_name' => 'k8s-pod-name',
643
+ 'container_name' => 'k8s-container-name'
644
+ },
645
+ 'docker' => { 'container_id' => 'e463bc0d3ae38f5c89d92dca49b30e049e899799920b79d4d5f705acbe82ba95' },
646
+ 'randomfield' => 'randomvalue'
647
+ }
648
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
649
+ { name: 'kubernetes_get_namespace_default', options: { allow_playback_repeats: true } },
650
+ { name: 'metadata_from_tag_and_journald_fields' }]) do
651
+ es = emit_with_tag(tag, msg, '
652
+ kubernetes_url https://localhost:8443
653
+ watch false
654
+ cache_size 1
655
+ lookup_from_k8s_field false
656
+ ')
657
+ expected_kube_metadata = {
658
+ 'docker' => {
659
+ 'container_id' => '838350c64bacba968d39a30a50789b2795291fceca6ccbff55298671d46b0e3b'
660
+ },
661
+ 'kubernetes' => {
662
+ 'host' => 'jimmi-redhat.localnet',
663
+ 'pod_name' => 'journald-pod-name',
664
+ 'container_name' => 'journald-container-name',
665
+ 'container_image' => 'journald-container-image:latest',
666
+ 'container_image_id' => 'docker://dda4c95705fb7050b701b10a7fe928ca5bc971a1dcb521ae3c339194cbf36b47',
667
+ 'namespace_name' => 'journald-namespace-name',
668
+ 'namespace_id' => '8282888f-733f-4f23-a3d3-1fdfa3bdacf2',
669
+ 'pod_id' => '5e1c1e27-b637-4e81-80b6-6d8a8c404d3b',
670
+ 'pod_ip' => '172.17.0.8',
671
+ 'master_url' => 'https://localhost:8443',
672
+ 'labels' => {
673
+ 'component' => 'journald-test'
674
+ }
675
+ }
676
+ }.merge(msg) { |key, oldval, newval| (key == 'kubernetes') || (key == 'docker') ? oldval : newval }
677
+ assert_equal(expected_kube_metadata, es[0])
678
+ end
679
+ end
680
+
681
+ test 'uses metadata from tag if use_journal false and lookup_from_k8s_field false' do
682
+ # with use_journal unset, should still use the journal fields instead of tag fields
683
+ tag = 'var.log.containers.fabric8-console-controller-98rqc_default_fabric8-console-container-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459.log'
684
+ msg = {
685
+ 'CONTAINER_NAME' => 'k8s_journald-container-name.db89db89_journald-pod-name_journald-namespace-name_c76927af-f563-11e4-b32d-54ee7527188d_89db89db',
686
+ 'CONTAINER_ID_FULL' => '838350c64bacba968d39a30a50789b2795291fceca6ccbff55298671d46b0e3b',
687
+ 'kubernetes' => {
688
+ 'namespace_name' => 'k8s-namespace-name',
689
+ 'pod_name' => 'k8s-pod-name',
690
+ 'container_name' => 'k8s-container-name'
691
+ },
692
+ 'docker' => { 'container_id' => 'e463bc0d3ae38f5c89d92dca49b30e049e899799920b79d4d5f705acbe82ba95' },
693
+ 'randomfield' => 'randomvalue'
694
+ }
695
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
696
+ { name: 'kubernetes_get_namespace_default', options: { allow_playback_repeats: true } },
697
+ { name: 'metadata_from_tag_and_journald_fields' }]) do
698
+ es = emit_with_tag(tag, msg, '
699
+ kubernetes_url https://localhost:8443
700
+ watch false
701
+ cache_size 1
702
+ lookup_from_k8s_field false
703
+ use_journal false
704
+ ')
705
+ expected_kube_metadata = {
706
+ 'docker' => {
707
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
708
+ },
709
+ 'kubernetes' => {
710
+ 'host' => 'jimmi-redhat.localnet',
711
+ 'pod_name' => 'fabric8-console-controller-98rqc',
712
+ 'container_name' => 'fabric8-console-container',
713
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
714
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
715
+ 'namespace_name' => 'default',
716
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
717
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
718
+ 'pod_ip' => '172.17.0.8',
719
+ 'master_url' => 'https://localhost:8443',
720
+ 'labels' => {
721
+ 'component' => 'fabric8Console'
722
+ }
723
+ }
724
+ }.merge(msg) { |key, oldval, newval| (key == 'kubernetes') || (key == 'docker') ? oldval : newval }
725
+ assert_equal(expected_kube_metadata, es[0])
726
+ end
727
+ end
728
+
729
+ test 'with kubernetes annotations' do
730
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
731
+ { name: 'kubernetes_docker_metadata_annotations' },
732
+ { name: 'kubernetes_get_namespace_default' }]) do
733
+ filtered = emit({}, '
734
+ kubernetes_url https://localhost:8443
735
+ watch false
736
+ cache_size 1
737
+ annotation_match [ "^custom.+", "two"]
738
+ ')
739
+ expected_kube_metadata = {
740
+ 'docker' => {
741
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
742
+ },
743
+ 'kubernetes' => {
744
+ 'host' => 'jimmi-redhat.localnet',
745
+ 'pod_name' => 'fabric8-console-controller-98rqc',
746
+ 'container_name' => 'fabric8-console-container',
747
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
748
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
749
+ 'namespace_name' => 'default',
750
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
751
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
752
+ 'pod_ip' => '172.17.0.8',
753
+ 'master_url' => 'https://localhost:8443',
754
+ 'labels' => {
755
+ 'component' => 'fabric8Console'
756
+ },
757
+ 'annotations' => {
758
+ 'custom_field1' => 'hello_kitty',
759
+ 'field_two' => 'value'
760
+ }
761
+ }
762
+ }
763
+ assert_equal(expected_kube_metadata, filtered[0])
764
+ end
765
+ end
766
+
767
+ test 'with records from journald and docker & kubernetes metadata, alternate form' do
768
+ # with use_journal true should ignore tags and use CONTAINER_NAME and CONTAINER_ID_FULL
769
+ tag = 'var.log.containers.junk1_junk2_junk3-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
770
+ msg = {
771
+ 'CONTAINER_NAME' => 'alt_fabric8-console-container_fabric8-console-controller-98rqc_default_c76927af-f563-11e4-b32d-54ee7527188d_0',
772
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
773
+ 'randomfield' => 'randomvalue'
774
+ }
775
+ VCR.use_cassettes([
776
+ { name: 'valid_kubernetes_api_server' },
777
+ { name: 'kubernetes_get_api_v1' },
778
+ { name: 'kubernetes_get_pod' },
779
+ { name: 'kubernetes_get_namespace_default' },
780
+ { name: 'metadata_from_tag_and_journald_fields' }
781
+ ]) do
782
+ filtered = emit_with_tag(tag, msg, '
783
+ kubernetes_url https://localhost:8443
784
+ watch false
785
+ cache_size 1
786
+ use_journal true
787
+ ')
788
+ expected_kube_metadata = {
789
+ 'docker' => {
790
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
791
+ },
792
+ 'kubernetes' => {
793
+ 'host' => 'jimmi-redhat.localnet',
794
+ 'pod_name' => 'fabric8-console-controller-98rqc',
795
+ 'container_name' => 'fabric8-console-container',
796
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
797
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
798
+ 'namespace_name' => 'default',
799
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
800
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
801
+ 'pod_ip' => '172.17.0.8',
802
+ 'master_url' => 'https://localhost:8443',
803
+ 'labels' => {
804
+ 'component' => 'fabric8Console'
805
+ }
806
+ }
807
+ }.merge(msg)
808
+ assert_equal(expected_kube_metadata, filtered[0])
809
+ end
810
+ end
811
+
812
+ test 'with kubernetes namespace annotations' do
813
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
814
+ { name: 'kubernetes_docker_metadata_annotations' },
815
+ { name: 'kubernetes_get_namespace_default' }]) do
816
+ filtered = emit({}, '
817
+ kubernetes_url https://localhost:8443
818
+ watch false
819
+ cache_size 1
820
+ annotation_match [ "^custom.+", "two", "workspace*"]
821
+ ')
822
+ expected_kube_metadata = {
823
+ 'docker' => {
824
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
825
+ },
826
+ 'kubernetes' => {
827
+ 'host' => 'jimmi-redhat.localnet',
828
+ 'pod_name' => 'fabric8-console-controller-98rqc',
829
+ 'container_name' => 'fabric8-console-container',
830
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
831
+ 'namespace_name' => 'default',
832
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
833
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
834
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
835
+ 'pod_ip' => '172.17.0.8',
836
+ 'master_url' => 'https://localhost:8443',
837
+ 'labels' => {
838
+ 'component' => 'fabric8Console'
839
+ },
840
+ 'annotations' => {
841
+ 'custom_field1' => 'hello_kitty',
842
+ 'field_two' => 'value'
843
+ },
844
+ 'namespace_annotations' => {
845
+ 'workspaceId' => 'myWorkspaceName'
846
+ }
847
+ }
848
+ }
849
+ assert_equal(expected_kube_metadata, filtered[0])
850
+ end
851
+ end
852
+
853
+ test 'with kubernetes namespace annotations no match' do
854
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
855
+ { name: 'kubernetes_docker_metadata_annotations' },
856
+ { name: 'kubernetes_get_namespace_default' }]) do
857
+ filtered = emit({}, '
858
+ kubernetes_url https://localhost:8443
859
+ watch false
860
+ cache_size 1
861
+ annotation_match [ "noMatch*"]
862
+ ')
863
+ expected_kube_metadata = {
864
+ 'docker' => {
865
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
866
+ },
867
+ 'kubernetes' => {
868
+ 'host' => 'jimmi-redhat.localnet',
869
+ 'pod_name' => 'fabric8-console-controller-98rqc',
870
+ 'container_name' => 'fabric8-console-container',
871
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
872
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
873
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
874
+ 'namespace_name' => 'default',
875
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
876
+ 'pod_ip' => '172.17.0.8',
877
+ 'master_url' => 'https://localhost:8443',
878
+ 'labels' => {
879
+ 'component' => 'fabric8Console'
880
+ }
881
+ }
882
+ }
883
+ assert_equal(expected_kube_metadata, filtered[0])
884
+ end
885
+ end
886
+
887
+ test 'with CONTAINER_NAME that does not match' do
888
+ tag = 'var.log.containers.junk4_junk5_junk6-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
889
+ msg = {
890
+ 'CONTAINER_NAME' => 'does_not_match',
891
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
892
+ 'randomfield' => 'randomvalue'
893
+ }
894
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
895
+ { name: 'kubernetes_docker_metadata_annotations' },
896
+ { name: 'kubernetes_get_namespace_default' }]) do
897
+ filtered = emit_with_tag(tag, msg, '
898
+ kubernetes_url https://localhost:8443
899
+ watch false
900
+ cache_size 1
901
+ use_journal true
902
+ ')
903
+ expected_kube_metadata = {
904
+ 'CONTAINER_NAME' => 'does_not_match',
905
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
906
+ 'randomfield' => 'randomvalue'
907
+ }
908
+ assert_equal(expected_kube_metadata, filtered[0])
909
+ end
910
+ end
911
+
912
+ test 'with CONTAINER_NAME starts with k8s_ that does not match' do
913
+ tag = 'var.log.containers.junk4_junk5_junk6-49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed450.log'
914
+ msg = {
915
+ 'CONTAINER_NAME' => 'k8s_doesnotmatch',
916
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
917
+ 'randomfield' => 'randomvalue'
918
+ }
919
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
920
+ { name: 'kubernetes_docker_metadata_annotations' },
921
+ { name: 'kubernetes_get_namespace_default' }]) do
922
+ filtered = emit_with_tag(tag, msg, '
923
+ kubernetes_url https://localhost:8443
924
+ watch false
925
+ cache_size 1
926
+ use_journal true
927
+ ')
928
+ expected_kube_metadata = {
929
+ 'CONTAINER_NAME' => 'k8s_doesnotmatch',
930
+ 'CONTAINER_ID_FULL' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459',
931
+ 'randomfield' => 'randomvalue'
932
+ }
933
+ assert_equal(expected_kube_metadata, filtered[0])
934
+ end
935
+ end
936
+
937
+ test 'processes all events when reading from MessagePackEventStream' do
938
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' },
939
+ { name: 'kubernetes_get_pod' },
940
+ { name: 'kubernetes_get_namespace_default' }]) do
941
+ entries = [[@time, { 'time' => '2015-05-08T09:22:01Z' }], [@time, { 'time' => '2015-05-08T09:22:01Z' }]]
942
+ array_stream = Fluent::ArrayEventStream.new(entries)
943
+ msgpack_stream = Fluent::MessagePackEventStream.new(array_stream.to_msgpack_stream)
944
+
945
+ d = create_driver('
946
+ kubernetes_url https://localhost:8443
947
+ watch false
948
+ cache_size 1
949
+ ')
950
+ d.run do
951
+ d.feed(DEFAULT_TAG, msgpack_stream)
952
+ end
953
+ filtered = d.filtered.map(&:last)
954
+
955
+ expected_kube_metadata = {
956
+ 'time' => '2015-05-08T09:22:01Z',
957
+ 'docker' => {
958
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
959
+ },
960
+ 'kubernetes' => {
961
+ 'host' => 'jimmi-redhat.localnet',
962
+ 'pod_name' => 'fabric8-console-controller-98rqc',
963
+ 'container_name' => 'fabric8-console-container',
964
+ 'container_image' => 'fabric8/hawtio-kubernetes:latest',
965
+ 'container_image_id' => 'docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303',
966
+ 'namespace_name' => 'default',
967
+ 'namespace_id' => '898268c8-4a36-11e5-9d81-42010af0194c',
968
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
969
+ 'pod_ip' => '172.17.0.8',
970
+ 'master_url' => 'https://localhost:8443',
971
+ 'labels' => {
972
+ 'component' => 'fabric8Console'
973
+ }
974
+ }
975
+ }
976
+
977
+ assert_equal(expected_kube_metadata, filtered[0])
978
+ assert_equal(expected_kube_metadata, filtered[1])
979
+ end
980
+ end
981
+
982
+ test 'with docker & kubernetes metadata using skip config params' do
983
+ VCR.use_cassettes([{ name: 'valid_kubernetes_api_server' }, { name: 'kubernetes_get_api_v1' }, { name: 'kubernetes_get_pod' },
984
+ { name: 'kubernetes_get_namespace_default' }]) do
985
+ filtered = emit({}, '
986
+ kubernetes_url https://localhost:8443
987
+ watch false
988
+ cache_size 1
989
+ skip_labels true
990
+ skip_container_metadata true
991
+ skip_master_url true
992
+ skip_namespace_metadata true
993
+ ')
994
+ expected_kube_metadata = {
995
+ 'docker' => {
996
+ 'container_id' => '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
997
+ },
998
+ 'kubernetes' => {
999
+ 'host' => 'jimmi-redhat.localnet',
1000
+ 'pod_name' => 'fabric8-console-controller-98rqc',
1001
+ 'container_name' => 'fabric8-console-container',
1002
+ 'namespace_name' => 'default',
1003
+ 'pod_id' => 'c76927af-f563-11e4-b32d-54ee7527188d',
1004
+ 'pod_ip' => '172.17.0.8'
1005
+ }
1006
+ }
1007
+
1008
+ assert_equal(expected_kube_metadata, filtered[0])
1009
+ end
1010
+ end
1011
+ end
1012
+ end