fluent-plugin-kubernetes_metadata_filter_splunk 2.2.0

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