fluent-plugin-kubernetes_metadata_filter 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 247710a5a22705fe46717e612a13998857661dc9
4
- data.tar.gz: 419da7d1297da104e6802110ad95df22608e5c41
3
+ metadata.gz: e3cac3ef5c79d4a00c3344458a5f341759319b70
4
+ data.tar.gz: d8770e74580e828b42fd450515f92d8fb50712db
5
5
  SHA512:
6
- metadata.gz: e4417767dddc3bfabb183a32d4f79cd3622541c531fb65205dacd0678280351476bcd2165c927ba524b41f7f1dd33ddfcbb4aafc33da302cb4a13247ae16d772
7
- data.tar.gz: 5e1dd6ba75d5ba6965b31bbbd98df670280bf07164680067439b662135d97fcecd96decd6a129b1dbd1a2114e5078ea7384a26f97c1a5792f6e8d7ff7578fc11
6
+ metadata.gz: b555868c660ac76fcb05c4ca0579a2c94d26166f0d49c0b70b7fd50c9b8e13c562e2caa76d4305fcc8f355a233a8eb0477b88514a13c3a8889e1ac2ff3a648a7
7
+ data.tar.gz: c5d175766caeeee49fc8896cb46e957cf8bba60964a6b0e9164b4efdbba8492469bf6de62e55364fb88bfccaa9ab7b00e183d4a46c219c4f3332f35d8c821b91
data/README.md CHANGED
@@ -14,7 +14,7 @@ Configuration options for fluent.conf are:
14
14
  * `kubernetes_url` - URL to the API server. Set this to retrieve further kubernetes metadata for logs from kubernetes API server
15
15
  * `apiVersion` - API version to use (default: `v1`)
16
16
  * `ca_file` - path to CA file for Kubernetes server certificate validation
17
- * `verify_ssl` - validate SSL certificates (default: true)
17
+ * `verify_ssl` - validate SSL certificates (default: `true`)
18
18
  * `client_cert` - path to a client cert file to authenticate to the API server
19
19
  * `client_key` - path to a client key file to authenticate to the API server
20
20
  * `bearer_token_file` - path to a file containing the bearer token to use for authentication
@@ -24,6 +24,8 @@ This must used named capture groups for `container_name`, `pod_name` & `namespac
24
24
  * `cache_ttl` - TTL in seconds of each cached element. Set to negative value to disable TTL eviction (default: `3600` - 1 hour)
25
25
  * `watch` - set up a watch on pods on the API server for updates to metadata (default: `true`)
26
26
  * `merge_json_log` - merge logs in JSON format as top level keys (default: `true`)
27
+ * `de_dot` - replace dots in labels with configured `de_dot_separator`, required for ElasticSearch 2.x compatibility (default: `true`)
28
+ * `de_dot_separator` - separator to use if `de_dot` is enabled (default: `_`)
27
29
 
28
30
  ```
29
31
  <source>
data/circle.yml CHANGED
@@ -1,3 +1,7 @@
1
+ machine:
2
+ ruby:
3
+ version: 2.2.3
4
+
1
5
  test:
2
6
  override:
3
7
  - bundle exec rake test
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |gem|
6
6
  gem.name = "fluent-plugin-kubernetes_metadata_filter"
7
- gem.version = "0.15.0"
7
+ gem.version = "0.16.0"
8
8
  gem.authors = ["Jimmi Dyson"]
9
9
  gem.email = ["jimmidyson@gmail.com"]
10
10
  gem.description = %q{Filter plugin to add Kubernetes metadata}
@@ -23,33 +23,39 @@ module Fluent
23
23
 
24
24
  Fluent::Plugin.register_filter('kubernetes_metadata', self)
25
25
 
26
- config_param :kubernetes_url, :string, default: ''
26
+ config_param :kubernetes_url, :string, default: nil
27
27
  config_param :cache_size, :integer, default: 1000
28
28
  config_param :cache_ttl, :integer, default: 60 * 60
29
29
  config_param :watch, :bool, default: true
30
30
  config_param :apiVersion, :string, default: 'v1'
31
- config_param :client_cert, :string, default: ''
32
- config_param :client_key, :string, default: ''
33
- config_param :ca_file, :string, default: ''
31
+ config_param :client_cert, :string, default: nil
32
+ config_param :client_key, :string, default: nil
33
+ config_param :ca_file, :string, default: nil
34
34
  config_param :verify_ssl, :bool, default: true
35
35
  config_param :tag_to_kubernetes_name_regexp,
36
36
  :string,
37
37
  :default => 'var\.log\.containers\.(?<pod_name>[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*)_(?<namespace>[^_]+)_(?<container_name>.+)-(?<docker_id>[a-z0-9]{64})\.log$'
38
- config_param :bearer_token_file, :string, default: ''
38
+ config_param :bearer_token_file, :string, default: nil
39
39
  config_param :merge_json_log, :bool, default: true
40
40
  config_param :include_namespace_id, :bool, default: false
41
41
  config_param :secret_dir, :string, default: '/var/run/secrets/kubernetes.io/serviceaccount'
42
+ config_param :de_dot, :bool, default: true
43
+ config_param :de_dot_separator, :string, default: '_'
42
44
 
43
45
  def get_metadata(namespace_name, pod_name, container_name)
44
46
  begin
45
47
  metadata = @client.get_pod(pod_name, namespace_name)
46
48
  return if !metadata
49
+ labels = metadata['metadata']['labels'].to_h
50
+ if @de_dot
51
+ self.de_dot!(labels)
52
+ end
47
53
  return {
48
54
  namespace_name: namespace_name,
49
55
  pod_id: metadata['metadata']['uid'],
50
56
  pod_name: pod_name,
51
57
  container_name: container_name,
52
- labels: metadata['metadata']['labels'].to_h,
58
+ labels: labels,
53
59
  host: metadata['spec']['nodeName']
54
60
  }
55
61
  rescue KubeException
@@ -68,6 +74,10 @@ module Fluent
68
74
  require 'active_support/core_ext/object/blank'
69
75
  require 'lru_redux'
70
76
 
77
+ if @de_dot && (@de_dot_separator =~ /\./).present?
78
+ raise Fluent::ConfigError, "Invalid de_dot_separator: cannot be or contain '.'"
79
+ end
80
+
71
81
  if @cache_ttl < 0
72
82
  @cache_ttl = :none
73
83
  end
@@ -78,10 +88,10 @@ module Fluent
78
88
  @tag_to_kubernetes_name_regexp_compiled = Regexp.compile(@tag_to_kubernetes_name_regexp)
79
89
 
80
90
  # Use Kubernetes default service account if we're in a pod.
81
- if !@kubernetes_url.present?
91
+ if @kubernetes_url.nil?
82
92
  env_host = ENV['KUBERNETES_SERVICE_HOST']
83
93
  env_port = ENV['KUBERNETES_SERVICE_PORT']
84
- if !env_host.nil? and !env_port.nil?
94
+ if env_host.present? && env_port.present?
85
95
  @kubernetes_url = "https://#{env_host}:#{env_port}/api"
86
96
  end
87
97
  end
@@ -204,6 +214,16 @@ module Fluent
204
214
  record
205
215
  end
206
216
 
217
+ def de_dot!(h)
218
+ h.keys.each do |ref|
219
+ if h[ref] && ref =~ /\./
220
+ v = h.delete(ref)
221
+ newref = ref.to_s.gsub('.', @de_dot_separator)
222
+ h[newref.to_sym] = v
223
+ end
224
+ end
225
+ end
226
+
207
227
  def start_watch
208
228
  resource_version = @client.get_pods.resourceVersion
209
229
  watcher = @client.watch_pods(resource_version)
@@ -211,24 +231,27 @@ module Fluent
211
231
  case notice.type
212
232
  when 'MODIFIED'
213
233
  if notice.object.status.containerStatuses
234
+ pod_cache_key = "#{notice.object['metadata']['namespace']}_#{notice.object['metadata']['name']}"
214
235
  notice.object.status.containerStatuses.each { |container_status|
215
- if container_status['containerId']
216
- containerId = container_status['containerId'].sub(/^docker:\/\//, '')
217
- cached = @cache[containerId]
218
- if cached
219
- # Only thing that can be modified is labels
220
- cached[:labels] = v.object.metadata.labels.to_h
221
- @cache[containerId] = cached
236
+ cache_key = "#{pod_cache_key}_#{container_status['name']}"
237
+ cached = @cache[cache_key]
238
+ if cached
239
+ # Only thing that can be modified is labels
240
+ labels = notice.object.metadata.labels.to_h
241
+ if @de_dot
242
+ self.de_dot!(labels)
222
243
  end
244
+ cached[:kubernetes][:labels] = labels
245
+ @cache[cache_key] = cached
223
246
  end
224
247
  }
225
248
  end
226
249
  when 'DELETED'
227
250
  if notice.object.status.containerStatuses
251
+ pod_cache_key = "#{notice.object['metadata']['namespace']}_#{notice.object['metadata']['name']}"
228
252
  notice.object.status.containerStatuses.each { |container_status|
229
- if container_status['containerId']
230
- @cache.delete(container_status['containerId'].sub(/^docker:\/\//, ''))
231
- end
253
+ cache_key = "#{pod_cache_key}_#{container_status['name']}"
254
+ @cache.delete(cache_key)
232
255
  }
233
256
  end
234
257
  else
@@ -245,7 +268,7 @@ module Fluent
245
268
  puts notice
246
269
  case notice.type
247
270
  when 'DELETED'
248
- @namespace_cache.delete(notice.object['metadata']['uid'])
271
+ @namespace_cache.delete(notice.object['metadata']['name'])
249
272
  else
250
273
  # We only care about each namespace's name and UID, neither of which
251
274
  # is modifiable, so we only have to care about deletions.
@@ -0,0 +1,180 @@
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
+ ---
20
+ http_interactions:
21
+ - request:
22
+ method: get
23
+ uri: https://localhost:8443/api
24
+ body:
25
+ encoding: US-ASCII
26
+ string: ''
27
+ headers:
28
+ Accept:
29
+ - "*/*; q=0.5, application/xml"
30
+ Accept-Encoding:
31
+ - gzip, deflate
32
+ User-Agent:
33
+ - Ruby
34
+ response:
35
+ status:
36
+ code: 200
37
+ message: OK
38
+ headers:
39
+ Content-Type:
40
+ - application/json
41
+ Date:
42
+ - Fri, 08 May 2015 10:35:37 GMT
43
+ Content-Length:
44
+ - '67'
45
+ body:
46
+ encoding: UTF-8
47
+ string: |-
48
+ {
49
+ "versions": [
50
+ "v1"
51
+ ]
52
+ }
53
+ http_version:
54
+ recorded_at: Fri, 08 May 2015 10:35:37 GMT
55
+ - request:
56
+ method: get
57
+ uri: https://localhost:8443/api/v1/namespaces/default/pods/fabric8-console-controller-98rqc
58
+ body:
59
+ encoding: US-ASCII
60
+ string: ''
61
+ headers:
62
+ Accept:
63
+ - "*/*; q=0.5, application/xml"
64
+ Accept-Encoding:
65
+ - gzip, deflate
66
+ User-Agent:
67
+ - Ruby
68
+ response:
69
+ status:
70
+ code: 200
71
+ message: OK
72
+ headers:
73
+ Content-Type:
74
+ - application/json
75
+ Date:
76
+ - Fri, 08 May 2015 10:35:37 GMT
77
+ Transfer-Encoding:
78
+ - chunked
79
+ body:
80
+ encoding: UTF-8
81
+ string: |-
82
+ {
83
+ "kind": "Pod",
84
+ "apiVersion": "v1",
85
+ "metadata": {
86
+ "name": "fabric8-console-controller-98rqc",
87
+ "generateName": "fabric8-console-controller-",
88
+ "namespace": "default",
89
+ "selfLink": "/api/v1/namespaces/default/pods/fabric8-console-controller-98rqc",
90
+ "uid": "c76927af-f563-11e4-b32d-54ee7527188d",
91
+ "resourceVersion": "122",
92
+ "creationTimestamp": "2015-05-08T09:22:42Z",
93
+ "labels": {
94
+ "kubernetes.io/test": "somevalue"
95
+ }
96
+ },
97
+ "spec": {
98
+ "volumes": [
99
+ {
100
+ "name": "openshift-cert-secrets",
101
+ "hostPath": null,
102
+ "emptyDir": null,
103
+ "gcePersistentDisk": null,
104
+ "gitRepo": null,
105
+ "secret": {
106
+ "secretName": "openshift-cert-secrets"
107
+ },
108
+ "nfs": null,
109
+ "iscsi": null,
110
+ "glusterfs": null
111
+ }
112
+ ],
113
+ "containers": [
114
+ {
115
+ "name": "fabric8-console-container",
116
+ "image": "fabric8/hawtio-kubernetes:latest",
117
+ "ports": [
118
+ {
119
+ "containerPort": 9090,
120
+ "protocol": "TCP"
121
+ }
122
+ ],
123
+ "env": [
124
+ {
125
+ "name": "OAUTH_CLIENT_ID",
126
+ "value": "fabric8-console"
127
+ },
128
+ {
129
+ "name": "OAUTH_AUTHORIZE_URI",
130
+ "value": "https://localhost:8443/oauth/authorize"
131
+ }
132
+ ],
133
+ "resources": {},
134
+ "volumeMounts": [
135
+ {
136
+ "name": "openshift-cert-secrets",
137
+ "readOnly": true,
138
+ "mountPath": "/etc/secret-volume"
139
+ }
140
+ ],
141
+ "terminationMessagePath": "/dev/termination-log",
142
+ "imagePullPolicy": "IfNotPresent",
143
+ "capabilities": {}
144
+ }
145
+ ],
146
+ "restartPolicy": "Always",
147
+ "dnsPolicy": "ClusterFirst",
148
+ "nodeName": "jimmi-redhat.localnet"
149
+ },
150
+ "status": {
151
+ "phase": "Running",
152
+ "Condition": [
153
+ {
154
+ "type": "Ready",
155
+ "status": "True"
156
+ }
157
+ ],
158
+ "hostIP": "172.17.42.1",
159
+ "podIP": "172.17.0.8",
160
+ "containerStatuses": [
161
+ {
162
+ "name": "fabric8-console-container",
163
+ "state": {
164
+ "running": {
165
+ "startedAt": "2015-05-08T09:22:44Z"
166
+ }
167
+ },
168
+ "lastState": {},
169
+ "ready": true,
170
+ "restartCount": 0,
171
+ "image": "fabric8/hawtio-kubernetes:latest",
172
+ "imageID": "docker://b2bd1a24a68356b2f30128e6e28e672c1ef92df0d9ec01ec0c7faea5d77d2303",
173
+ "containerID": "docker://49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459"
174
+ }
175
+ ]
176
+ }
177
+ }
178
+ http_version:
179
+ recorded_at: Fri, 08 May 2015 10:35:37 GMT
180
+ recorded_with: VCR 2.9.3
@@ -112,10 +112,15 @@ class KubernetesMetadataFilterTest < Test::Unit::TestCase
112
112
  ENV['KUBERNETES_SERVICE_HOST'] = 'localhost'
113
113
  ENV['KUBERNETES_SERVICE_PORT'] = '8443'
114
114
 
115
- d = create_driver('watch false')
116
- assert_equal(d.instance.kubernetes_url, "https://localhost:8443/api")
117
- assert_false(d.instance.ca_file.present?)
118
- assert_false(d.instance.bearer_token_file.present?)
115
+ Dir.mktmpdir { |dir|
116
+ d = create_driver("
117
+ watch false
118
+ secret_dir #{dir}
119
+ ")
120
+ assert_equal(d.instance.kubernetes_url, "https://localhost:8443/api")
121
+ assert_false(d.instance.ca_file.present?)
122
+ assert_false(d.instance.bearer_token_file.present?)
123
+ }
119
124
  ensure
120
125
  ENV['KUBERNETES_SERVICE_HOST'] = nil
121
126
  ENV['KUBERNETES_SERVICE_PORT'] = nil
@@ -297,5 +302,62 @@ class KubernetesMetadataFilterTest < Test::Unit::TestCase
297
302
  es = emit_with_tag('non-kubernetes', msg, '')
298
303
  assert_equal(msg.merge(json_log), es.instance_variable_get(:@record_array)[0])
299
304
  end
305
+
306
+ test 'with kubernetes dotted labels, de_dot enabled' do
307
+ VCR.use_cassette('kubernetes_docker_metadata_dotted_labels') do
308
+ es = emit()
309
+ expected_kube_metadata = {
310
+ docker: {
311
+ container_id: '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
312
+ },
313
+ kubernetes: {
314
+ host: 'jimmi-redhat.localnet',
315
+ pod_name: 'fabric8-console-controller-98rqc',
316
+ container_name: 'fabric8-console-container',
317
+ namespace_name: 'default',
318
+ pod_id: 'c76927af-f563-11e4-b32d-54ee7527188d',
319
+ labels: {
320
+ 'kubernetes_io/test': 'somevalue'
321
+ }
322
+ }
323
+ }
324
+ assert_equal(expected_kube_metadata, es.instance_variable_get(:@record_array)[0])
325
+ end
326
+ end
327
+
328
+ test 'with kubernetes dotted labels, de_dot disabled' do
329
+ VCR.use_cassette('kubernetes_docker_metadata_dotted_labels') do
330
+ es = emit({}, '
331
+ kubernetes_url https://localhost:8443
332
+ watch false
333
+ cache_size 1
334
+ de_dot false
335
+ ')
336
+ expected_kube_metadata = {
337
+ docker: {
338
+ container_id: '49095a2894da899d3b327c5fde1e056a81376cc9a8f8b09a195f2a92bceed459'
339
+ },
340
+ kubernetes: {
341
+ host: 'jimmi-redhat.localnet',
342
+ pod_name: 'fabric8-console-controller-98rqc',
343
+ container_name: 'fabric8-console-container',
344
+ namespace_name: 'default',
345
+ pod_id: 'c76927af-f563-11e4-b32d-54ee7527188d',
346
+ labels: {
347
+ 'kubernetes.io/test': 'somevalue'
348
+ }
349
+ }
350
+ }
351
+ assert_equal(expected_kube_metadata, es.instance_variable_get(:@record_array)[0])
352
+ end
353
+ end
354
+
355
+ test 'invalid de_dot_separator config' do
356
+ assert_raise Fluent::ConfigError do
357
+ create_driver('
358
+ de_dot_separator contains.
359
+ ')
360
+ end
361
+ end
300
362
  end
301
363
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-kubernetes_metadata_filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jimmi Dyson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-27 00:00:00.000000000 Z
11
+ date: 2016-02-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: fluentd
@@ -224,6 +224,7 @@ files:
224
224
  - lib/fluent/plugin/filter_kubernetes_metadata.rb
225
225
  - test/cassettes/invalid_api_server_config.yml
226
226
  - test/cassettes/kubernetes_docker_metadata.yml
227
+ - test/cassettes/kubernetes_docker_metadata_dotted_labels.yml
227
228
  - test/cassettes/kubernetes_docker_metadata_using_bearer_token.yml
228
229
  - test/cassettes/metadata_with_namespace_id.yml
229
230
  - test/cassettes/non_kubernetes_docker_metadata.yml
@@ -258,6 +259,7 @@ summary: Filter plugin to add Kubernetes metadata
258
259
  test_files:
259
260
  - test/cassettes/invalid_api_server_config.yml
260
261
  - test/cassettes/kubernetes_docker_metadata.yml
262
+ - test/cassettes/kubernetes_docker_metadata_dotted_labels.yml
261
263
  - test/cassettes/kubernetes_docker_metadata_using_bearer_token.yml
262
264
  - test/cassettes/metadata_with_namespace_id.yml
263
265
  - test/cassettes/non_kubernetes_docker_metadata.yml