fluent-plugin-kubernetes_metadata_filter-rh 2.6.1

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.
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