fluent-plugin-kubernetes_metadata_filter_v0.14 0.24.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +201 -0
- data/README.md +141 -0
- data/Rakefile +37 -0
- data/circle.yml +7 -0
- data/fluent-plugin-kubernetes_metadata_filter.gemspec +38 -0
- data/lib/fluent/plugin/filter_kubernetes_metadata.rb +417 -0
- data/test/cassettes/invalid_api_server_config.yml +53 -0
- data/test/cassettes/kubernetes_docker_metadata.yml +180 -0
- data/test/cassettes/kubernetes_docker_metadata_annotations.yml +188 -0
- data/test/cassettes/kubernetes_docker_metadata_dotted_labels.yml +180 -0
- data/test/cassettes/kubernetes_docker_metadata_using_bearer_token.yml +200 -0
- data/test/cassettes/metadata_with_namespace_id.yml +228 -0
- data/test/cassettes/non_kubernetes_docker_metadata.yml +97 -0
- data/test/cassettes/valid_kubernetes_api_server.yml +55 -0
- data/test/helper.rb +63 -0
- data/test/plugin/test.token +1 -0
- data/test/plugin/test_filter_kubernetes_metadata.rb +537 -0
- metadata +270 -0
|
@@ -0,0 +1,417 @@
|
|
|
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
|
+
module Fluent
|
|
20
|
+
class KubernetesMetadataFilter < Fluent::Filter
|
|
21
|
+
K8_POD_CA_CERT = 'ca.crt'
|
|
22
|
+
K8_POD_TOKEN = 'token'
|
|
23
|
+
|
|
24
|
+
Fluent::Plugin.register_filter('kubernetes_metadata', self)
|
|
25
|
+
|
|
26
|
+
config_param :kubernetes_url, :string, default: nil
|
|
27
|
+
config_param :cache_size, :integer, default: 1000
|
|
28
|
+
config_param :cache_ttl, :integer, default: 60 * 60
|
|
29
|
+
config_param :watch, :bool, default: true
|
|
30
|
+
config_param :apiVersion, :string, default: 'v1'
|
|
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
|
+
config_param :verify_ssl, :bool, default: true
|
|
35
|
+
config_param :tag_to_kubernetes_name_regexp,
|
|
36
|
+
:string,
|
|
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: nil
|
|
39
|
+
config_param :merge_json_log, :bool, default: true
|
|
40
|
+
config_param :preserve_json_log, :bool, default: true
|
|
41
|
+
config_param :include_namespace_id, :bool, default: false
|
|
42
|
+
config_param :secret_dir, :string, default: '/var/run/secrets/kubernetes.io/serviceaccount'
|
|
43
|
+
config_param :de_dot, :bool, default: true
|
|
44
|
+
config_param :de_dot_separator, :string, default: '_'
|
|
45
|
+
# if reading from the journal, the record will contain the following fields in the following
|
|
46
|
+
# format:
|
|
47
|
+
# CONTAINER_NAME=k8s_$containername.$containerhash_$podname_$namespacename_$poduuid_$rand32bitashex
|
|
48
|
+
# CONTAINER_FULL_ID=dockeridassha256hexvalue
|
|
49
|
+
config_param :use_journal, :bool, default: false
|
|
50
|
+
# Field 2 is the container_hash, field 5 is the pod_id, and field 6 is the pod_randhex
|
|
51
|
+
# I would have included them as named groups, but you can't have named groups that are
|
|
52
|
+
# non-capturing :P
|
|
53
|
+
config_param :container_name_to_kubernetes_regexp,
|
|
54
|
+
:string,
|
|
55
|
+
:default => '^k8s_(?<container_name>[^\.]+)\.[^_]+_(?<pod_name>[^_]+)_(?<namespace>[^_]+)_[^_]+_[a-f0-9]{8}$'
|
|
56
|
+
|
|
57
|
+
config_param :annotation_match, :array, default: []
|
|
58
|
+
|
|
59
|
+
def syms_to_strs(hsh)
|
|
60
|
+
newhsh = {}
|
|
61
|
+
hsh.each_pair do |kk,vv|
|
|
62
|
+
if vv.is_a?(Hash)
|
|
63
|
+
vv = syms_to_strs(vv)
|
|
64
|
+
end
|
|
65
|
+
if kk.is_a?(Symbol)
|
|
66
|
+
newhsh[kk.to_s] = vv
|
|
67
|
+
else
|
|
68
|
+
newhsh[kk] = vv
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
newhsh
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def get_metadata(namespace_name, pod_name, container_name)
|
|
75
|
+
begin
|
|
76
|
+
metadata = @client.get_pod(pod_name, namespace_name)
|
|
77
|
+
return if !metadata
|
|
78
|
+
labels = syms_to_strs(metadata['metadata']['labels'].to_h)
|
|
79
|
+
annotations = match_annotations(syms_to_strs(metadata['metadata']['annotations'].to_h))
|
|
80
|
+
if @de_dot
|
|
81
|
+
self.de_dot!(labels)
|
|
82
|
+
end
|
|
83
|
+
kubernetes_metadata = {
|
|
84
|
+
'namespace_name' => namespace_name,
|
|
85
|
+
'pod_id' => metadata['metadata']['uid'],
|
|
86
|
+
'pod_name' => pod_name,
|
|
87
|
+
'container_name' => container_name,
|
|
88
|
+
'labels' => labels,
|
|
89
|
+
'host' => metadata['spec']['nodeName']
|
|
90
|
+
}
|
|
91
|
+
kubernetes_metadata['annotations'] = annotations unless annotations.empty?
|
|
92
|
+
return kubernetes_metadata
|
|
93
|
+
rescue KubeException
|
|
94
|
+
nil
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def initialize
|
|
99
|
+
super
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def filter(tag, time, record)
|
|
103
|
+
record
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def configure(conf)
|
|
107
|
+
super
|
|
108
|
+
|
|
109
|
+
require 'kubeclient'
|
|
110
|
+
require 'active_support/core_ext/object/blank'
|
|
111
|
+
require 'lru_redux'
|
|
112
|
+
|
|
113
|
+
if @de_dot && (@de_dot_separator =~ /\./).present?
|
|
114
|
+
raise Fluent::ConfigError, "Invalid de_dot_separator: cannot be or contain '.'"
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
if @cache_ttl < 0
|
|
118
|
+
@cache_ttl = :none
|
|
119
|
+
end
|
|
120
|
+
@cache = LruRedux::TTL::ThreadSafeCache.new(@cache_size, @cache_ttl)
|
|
121
|
+
if @include_namespace_id
|
|
122
|
+
@namespace_cache = LruRedux::TTL::ThreadSafeCache.new(@cache_size, @cache_ttl)
|
|
123
|
+
end
|
|
124
|
+
@tag_to_kubernetes_name_regexp_compiled = Regexp.compile(@tag_to_kubernetes_name_regexp)
|
|
125
|
+
@container_name_to_kubernetes_regexp_compiled = Regexp.compile(@container_name_to_kubernetes_regexp)
|
|
126
|
+
|
|
127
|
+
# Use Kubernetes default service account if we're in a pod.
|
|
128
|
+
if @kubernetes_url.nil?
|
|
129
|
+
env_host = ENV['KUBERNETES_SERVICE_HOST']
|
|
130
|
+
env_port = ENV['KUBERNETES_SERVICE_PORT']
|
|
131
|
+
if env_host.present? && env_port.present?
|
|
132
|
+
@kubernetes_url = "https://#{env_host}:#{env_port}/api"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
# Use SSL certificate and bearer token from Kubernetes service account.
|
|
137
|
+
if Dir.exist?(@secret_dir)
|
|
138
|
+
ca_cert = File.join(@secret_dir, K8_POD_CA_CERT)
|
|
139
|
+
pod_token = File.join(@secret_dir, K8_POD_TOKEN)
|
|
140
|
+
|
|
141
|
+
if !@ca_file.present? and File.exist?(ca_cert)
|
|
142
|
+
@ca_file = ca_cert
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
if !@bearer_token_file.present? and File.exist?(pod_token)
|
|
146
|
+
@bearer_token_file = pod_token
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
if @kubernetes_url.present?
|
|
151
|
+
|
|
152
|
+
ssl_options = {
|
|
153
|
+
client_cert: @client_cert.present? ? OpenSSL::X509::Certificate.new(File.read(@client_cert)) : nil,
|
|
154
|
+
client_key: @client_key.present? ? OpenSSL::PKey::RSA.new(File.read(@client_key)) : nil,
|
|
155
|
+
ca_file: @ca_file,
|
|
156
|
+
verify_ssl: @verify_ssl ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
auth_options = {}
|
|
160
|
+
|
|
161
|
+
if @bearer_token_file.present?
|
|
162
|
+
bearer_token = File.read(@bearer_token_file)
|
|
163
|
+
auth_options[:bearer_token] = bearer_token
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
@client = Kubeclient::Client.new @kubernetes_url, @apiVersion,
|
|
167
|
+
ssl_options: ssl_options,
|
|
168
|
+
auth_options: auth_options
|
|
169
|
+
|
|
170
|
+
begin
|
|
171
|
+
@client.api_valid?
|
|
172
|
+
rescue KubeException => kube_error
|
|
173
|
+
raise Fluent::ConfigError, "Invalid Kubernetes API #{@apiVersion} endpoint #{@kubernetes_url}: #{kube_error.message}"
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if @watch
|
|
177
|
+
thread = Thread.new(self) { |this| this.start_watch }
|
|
178
|
+
thread.abort_on_exception = true
|
|
179
|
+
if @include_namespace_id
|
|
180
|
+
namespace_thread = Thread.new(self) { |this| this.start_namespace_watch }
|
|
181
|
+
namespace_thread.abort_on_exception = true
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
if @use_journal
|
|
186
|
+
@merge_json_log_key = 'MESSAGE'
|
|
187
|
+
self.class.class_eval { alias_method :filter_stream, :filter_stream_from_journal }
|
|
188
|
+
else
|
|
189
|
+
@merge_json_log_key = 'log'
|
|
190
|
+
self.class.class_eval { alias_method :filter_stream, :filter_stream_from_files }
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
@annotations_regexps = []
|
|
194
|
+
@annotation_match.each do |regexp|
|
|
195
|
+
begin
|
|
196
|
+
@annotations_regexps << Regexp.compile(regexp)
|
|
197
|
+
rescue RegexpError => e
|
|
198
|
+
log.error "Error: invalid regular expression in annotation_match: #{e}"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def filter_stream_from_files(tag, es)
|
|
205
|
+
new_es = MultiEventStream.new
|
|
206
|
+
|
|
207
|
+
match_data = tag.match(@tag_to_kubernetes_name_regexp_compiled)
|
|
208
|
+
|
|
209
|
+
if match_data
|
|
210
|
+
metadata = {
|
|
211
|
+
'docker' => {
|
|
212
|
+
'container_id' => match_data['docker_id']
|
|
213
|
+
},
|
|
214
|
+
'kubernetes' => {
|
|
215
|
+
'namespace_name' => match_data['namespace'],
|
|
216
|
+
'pod_name' => match_data['pod_name'],
|
|
217
|
+
'container_name' => match_data['container_name']
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if @kubernetes_url.present?
|
|
222
|
+
cache_key = "#{metadata['kubernetes']['namespace_name']}_#{metadata['kubernetes']['pod_name']}_#{metadata['kubernetes']['container_name']}"
|
|
223
|
+
|
|
224
|
+
this = self
|
|
225
|
+
metadata = @cache.getset(cache_key) {
|
|
226
|
+
if metadata
|
|
227
|
+
kubernetes_metadata = this.get_metadata(
|
|
228
|
+
metadata['kubernetes']['namespace_name'],
|
|
229
|
+
metadata['kubernetes']['pod_name'],
|
|
230
|
+
metadata['kubernetes']['container_name']
|
|
231
|
+
)
|
|
232
|
+
metadata['kubernetes'] = kubernetes_metadata if kubernetes_metadata
|
|
233
|
+
metadata
|
|
234
|
+
end
|
|
235
|
+
}
|
|
236
|
+
if @include_namespace_id
|
|
237
|
+
namespace_name = metadata['kubernetes']['namespace_name']
|
|
238
|
+
namespace_id = @namespace_cache.getset(namespace_name) {
|
|
239
|
+
namespace = @client.get_namespace(namespace_name)
|
|
240
|
+
namespace['metadata']['uid'] if namespace
|
|
241
|
+
}
|
|
242
|
+
metadata['kubernetes']['namespace_id'] = namespace_id if namespace_id
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
es.each { |time, record|
|
|
248
|
+
record = merge_json_log(record) if @merge_json_log
|
|
249
|
+
|
|
250
|
+
record = record.merge(metadata) if metadata
|
|
251
|
+
|
|
252
|
+
new_es.add(time, record)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
new_es
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def filter_stream_from_journal(tag, es)
|
|
259
|
+
new_es = MultiEventStream.new
|
|
260
|
+
|
|
261
|
+
es.each { |time, record|
|
|
262
|
+
record = merge_json_log(record) if @merge_json_log
|
|
263
|
+
|
|
264
|
+
metadata = nil
|
|
265
|
+
if record.has_key?('CONTAINER_NAME') && record.has_key?('CONTAINER_ID_FULL')
|
|
266
|
+
metadata = record['CONTAINER_NAME'].match(@container_name_to_kubernetes_regexp_compiled) do |match_data|
|
|
267
|
+
metadata = {
|
|
268
|
+
'docker' => {
|
|
269
|
+
'container_id' => record['CONTAINER_ID_FULL']
|
|
270
|
+
},
|
|
271
|
+
'kubernetes' => {
|
|
272
|
+
'namespace_name' => match_data['namespace'],
|
|
273
|
+
'pod_name' => match_data['pod_name'],
|
|
274
|
+
'container_name' => match_data['container_name']
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if @kubernetes_url.present?
|
|
278
|
+
cache_key = "#{metadata['kubernetes']['namespace_name']}_#{metadata['kubernetes']['pod_name']}_#{metadata['kubernetes']['container_name']}"
|
|
279
|
+
|
|
280
|
+
this = self
|
|
281
|
+
metadata = @cache.getset(cache_key) {
|
|
282
|
+
if metadata
|
|
283
|
+
kubernetes_metadata = this.get_metadata(
|
|
284
|
+
metadata['kubernetes']['namespace_name'],
|
|
285
|
+
metadata['kubernetes']['pod_name'],
|
|
286
|
+
metadata['kubernetes']['container_name']
|
|
287
|
+
)
|
|
288
|
+
metadata['kubernetes'] = kubernetes_metadata if kubernetes_metadata
|
|
289
|
+
metadata
|
|
290
|
+
end
|
|
291
|
+
}
|
|
292
|
+
if @include_namespace_id
|
|
293
|
+
namespace_name = metadata['kubernetes']['namespace_name']
|
|
294
|
+
namespace_id = @namespace_cache.getset(namespace_name) {
|
|
295
|
+
namespace = @client.get_namespace(namespace_name)
|
|
296
|
+
namespace['metadata']['uid'] if namespace
|
|
297
|
+
}
|
|
298
|
+
metadata['kubernetes']['namespace_id'] = namespace_id if namespace_id
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
metadata
|
|
302
|
+
end
|
|
303
|
+
unless metadata
|
|
304
|
+
log.debug "Error: could not match CONTAINER_NAME from record #{record}"
|
|
305
|
+
end
|
|
306
|
+
elsif record.has_key?('CONTAINER_NAME') && record['CONTAINER_NAME'].start_with?('k8s_')
|
|
307
|
+
log.debug "Error: no container name and id in record #{record}"
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
if metadata
|
|
311
|
+
record = record.merge(metadata)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
new_es.add(time, record)
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
new_es
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
def merge_json_log(record)
|
|
321
|
+
if record.has_key?(@merge_json_log_key)
|
|
322
|
+
log = record[@merge_json_log_key].strip
|
|
323
|
+
if log[0].eql?('{') && log[-1].eql?('}')
|
|
324
|
+
begin
|
|
325
|
+
record = JSON.parse(log).merge(record)
|
|
326
|
+
unless @preserve_json_log
|
|
327
|
+
record.delete(@merge_json_log_key)
|
|
328
|
+
end
|
|
329
|
+
rescue JSON::ParserError
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
record
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def de_dot!(h)
|
|
337
|
+
h.keys.each do |ref|
|
|
338
|
+
if h[ref] && ref =~ /\./
|
|
339
|
+
v = h.delete(ref)
|
|
340
|
+
newref = ref.to_s.gsub('.', @de_dot_separator)
|
|
341
|
+
h[newref] = v
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def match_annotations(annotations)
|
|
347
|
+
result = {}
|
|
348
|
+
@annotations_regexps.each do |regexp|
|
|
349
|
+
annotations.each do |key, value|
|
|
350
|
+
if ::Fluent::StringUtil.match_regexp(regexp, key.to_s)
|
|
351
|
+
result[key] = value
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
result
|
|
356
|
+
end
|
|
357
|
+
|
|
358
|
+
def start_watch
|
|
359
|
+
begin
|
|
360
|
+
resource_version = @client.get_pods.resourceVersion
|
|
361
|
+
watcher = @client.watch_pods(resource_version)
|
|
362
|
+
rescue Exception => e
|
|
363
|
+
raise Fluent::ConfigError, "Exception encountered fetching metadata from Kubernetes API endpoint: #{e.message}"
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
watcher.each do |notice|
|
|
367
|
+
case notice.type
|
|
368
|
+
when 'MODIFIED'
|
|
369
|
+
if notice.object.status.containerStatuses
|
|
370
|
+
pod_cache_key = "#{notice.object['metadata']['namespace']}_#{notice.object['metadata']['name']}"
|
|
371
|
+
notice.object.status.containerStatuses.each { |container_status|
|
|
372
|
+
cache_key = "#{pod_cache_key}_#{container_status['name']}"
|
|
373
|
+
cached = @cache[cache_key]
|
|
374
|
+
if cached
|
|
375
|
+
# Only thing that can be modified is labels and (possibly) annotations
|
|
376
|
+
labels = syms_to_strs(notice.object.metadata.labels.to_h)
|
|
377
|
+
annotations = match_annotations(syms_to_strs(notice.object.metadata.annotations.to_h))
|
|
378
|
+
if @de_dot
|
|
379
|
+
self.de_dot!(labels)
|
|
380
|
+
end
|
|
381
|
+
cached['kubernetes']['labels'] = labels
|
|
382
|
+
cached['kubernetes']['annotations'] = annotations unless annotations.empty?
|
|
383
|
+
@cache[cache_key] = cached
|
|
384
|
+
end
|
|
385
|
+
}
|
|
386
|
+
end
|
|
387
|
+
when 'DELETED'
|
|
388
|
+
if notice.object.status.containerStatuses
|
|
389
|
+
pod_cache_key = "#{notice.object['metadata']['namespace']}_#{notice.object['metadata']['name']}"
|
|
390
|
+
notice.object.status.containerStatuses.each { |container_status|
|
|
391
|
+
cache_key = "#{pod_cache_key}_#{container_status['name']}"
|
|
392
|
+
@cache.delete(cache_key)
|
|
393
|
+
}
|
|
394
|
+
end
|
|
395
|
+
else
|
|
396
|
+
# Don't pay attention to creations, since the created pod may not
|
|
397
|
+
# end up on this node.
|
|
398
|
+
end
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
def start_namespace_watch
|
|
403
|
+
resource_version = @client.get_namespaces.resourceVersion
|
|
404
|
+
watcher = @client.watch_namespaces(resource_version)
|
|
405
|
+
watcher.each do |notice|
|
|
406
|
+
puts notice
|
|
407
|
+
case notice.type
|
|
408
|
+
when 'DELETED'
|
|
409
|
+
@namespace_cache.delete(notice.object['metadata']['name'])
|
|
410
|
+
else
|
|
411
|
+
# We only care about each namespace's name and UID, neither of which
|
|
412
|
+
# is modifiable, so we only have to care about deletions.
|
|
413
|
+
end
|
|
414
|
+
end
|
|
415
|
+
end
|
|
416
|
+
end
|
|
417
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
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
|
+
Authorization:
|
|
35
|
+
- Bearer YzYyYzFlODMtODdhNS00ZTMyLWIzMmItNmY4NDc4OTI1ZWF
|
|
36
|
+
response:
|
|
37
|
+
status:
|
|
38
|
+
code: 401
|
|
39
|
+
message: Unauthorized
|
|
40
|
+
headers:
|
|
41
|
+
Content-Type:
|
|
42
|
+
- text/plain; charset=utf-8
|
|
43
|
+
Date:
|
|
44
|
+
- Sat, 09 May 2015 14:04:39 GMT
|
|
45
|
+
Content-Length:
|
|
46
|
+
- '13'
|
|
47
|
+
body:
|
|
48
|
+
encoding: UTF-8
|
|
49
|
+
string: |
|
|
50
|
+
Unauthorized
|
|
51
|
+
http_version:
|
|
52
|
+
recorded_at: Sat, 09 May 2015 14:04:39 GMT
|
|
53
|
+
recorded_with: VCR 2.9.3
|