fluent-plugin-vadimberezniker-gcp 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CONTRIBUTING +24 -0
- data/Gemfile +3 -0
- data/LICENSE +201 -0
- data/README.rdoc +53 -0
- data/Rakefile +43 -0
- data/fluent-plugin-google-cloud.gemspec +43 -0
- data/fluent-plugin-vadimberezniker-gcp-0.13.2.gem +0 -0
- data/lib/fluent/plugin/common.rb +399 -0
- data/lib/fluent/plugin/filter_add_insert_ids.rb +86 -0
- data/lib/fluent/plugin/filter_analyze_config.rb +410 -0
- data/lib/fluent/plugin/in_object_space_dump.rb +62 -0
- data/lib/fluent/plugin/monitoring.rb +265 -0
- data/lib/fluent/plugin/out_google_cloud.rb +2209 -0
- data/lib/fluent/plugin/statusz.rb +124 -0
- data/test/helper.rb +46 -0
- data/test/plugin/asserts.rb +87 -0
- data/test/plugin/base_test.rb +2680 -0
- data/test/plugin/constants.rb +1114 -0
- data/test/plugin/data/c31e573fd7f62ed495c9ca3821a5a85cb036dee1-privatekey.p12 +0 -0
- data/test/plugin/data/credentials.json +7 -0
- data/test/plugin/data/google-fluentd-baseline.conf +24 -0
- data/test/plugin/data/google-fluentd-custom.conf +40 -0
- data/test/plugin/data/iam-credentials.json +11 -0
- data/test/plugin/data/invalid_credentials.json +8 -0
- data/test/plugin/data/new-style-credentials.json +12 -0
- data/test/plugin/test_driver.rb +56 -0
- data/test/plugin/test_filter_add_insert_ids.rb +137 -0
- data/test/plugin/test_filter_analyze_config.rb +257 -0
- data/test/plugin/test_out_google_cloud.rb +465 -0
- data/test/plugin/test_out_google_cloud_grpc.rb +478 -0
- data/test/plugin/utils.rb +148 -0
- metadata +347 -0
@@ -0,0 +1,465 @@
|
|
1
|
+
# Copyright 2014 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'fluent/test/startup_shutdown'
|
16
|
+
require 'net/http'
|
17
|
+
|
18
|
+
require_relative 'base_test'
|
19
|
+
require_relative 'test_driver'
|
20
|
+
|
21
|
+
# Unit tests for Google Cloud Logging plugin
|
22
|
+
class GoogleCloudOutputTest < Test::Unit::TestCase
|
23
|
+
include BaseTest
|
24
|
+
extend Fluent::Test::StartupShutdown
|
25
|
+
|
26
|
+
def test_configure_use_grpc
|
27
|
+
setup_gce_metadata_stubs
|
28
|
+
d = create_driver
|
29
|
+
assert_false d.instance.instance_variable_get(:@use_grpc)
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_user_agent
|
33
|
+
setup_gce_metadata_stubs
|
34
|
+
user_agent = nil
|
35
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI).to_return do |request|
|
36
|
+
user_agent = request.headers['User-Agent']
|
37
|
+
{ body: '' }
|
38
|
+
end
|
39
|
+
d = create_driver
|
40
|
+
d.emit('message' => log_entry(0))
|
41
|
+
d.run
|
42
|
+
assert_match Regexp.new("#{Fluent::GoogleCloudOutput::PLUGIN_NAME}/" \
|
43
|
+
"#{Fluent::GoogleCloudOutput::PLUGIN_VERSION}"), \
|
44
|
+
user_agent
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_client_status400
|
48
|
+
setup_gce_metadata_stubs
|
49
|
+
# The API Client should not retry this and the plugin should consume
|
50
|
+
# the exception.
|
51
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI)
|
52
|
+
.to_return(status: 400, body: 'Bad Request')
|
53
|
+
d = create_driver
|
54
|
+
d.emit('message' => log_entry(0))
|
55
|
+
d.run
|
56
|
+
assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 1)
|
57
|
+
end
|
58
|
+
|
59
|
+
# All credentials errors resolve to a 401.
|
60
|
+
def test_client_status401
|
61
|
+
setup_gce_metadata_stubs
|
62
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI)
|
63
|
+
.to_return(status: 401, body: 'Unauthorized')
|
64
|
+
d = create_driver
|
65
|
+
d.emit('message' => log_entry(0))
|
66
|
+
begin
|
67
|
+
d.run
|
68
|
+
rescue Google::Apis::AuthorizationError => e
|
69
|
+
assert_equal 'Unauthorized', e.message
|
70
|
+
end
|
71
|
+
assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 2)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_partial_success
|
75
|
+
setup_gce_metadata_stubs
|
76
|
+
clear_metrics
|
77
|
+
# The API Client should not retry this and the plugin should consume
|
78
|
+
# the exception.
|
79
|
+
root_error_code = PARTIAL_SUCCESS_RESPONSE_BODY['error']['code']
|
80
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI)
|
81
|
+
.to_return(status: root_error_code,
|
82
|
+
body: PARTIAL_SUCCESS_RESPONSE_BODY.to_json)
|
83
|
+
d = create_driver(ENABLE_PROMETHEUS_CONFIG)
|
84
|
+
4.times do |i|
|
85
|
+
d.emit('message' => log_entry(i.to_s))
|
86
|
+
end
|
87
|
+
d.run
|
88
|
+
assert_prometheus_metric_value(
|
89
|
+
:stackdriver_successful_requests_count, 1,
|
90
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
91
|
+
grpc: false, code: 200
|
92
|
+
)
|
93
|
+
assert_prometheus_metric_value(
|
94
|
+
:stackdriver_ingested_entries_count, 1,
|
95
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
96
|
+
grpc: false, code: 200
|
97
|
+
)
|
98
|
+
assert_prometheus_metric_value(
|
99
|
+
:stackdriver_dropped_entries_count, 2,
|
100
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
101
|
+
grpc: false, code: 3
|
102
|
+
)
|
103
|
+
assert_prometheus_metric_value(
|
104
|
+
:stackdriver_dropped_entries_count, 1,
|
105
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
106
|
+
grpc: false, code: 7
|
107
|
+
)
|
108
|
+
assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 1)
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_non_api_error
|
112
|
+
setup_gce_metadata_stubs
|
113
|
+
clear_metrics
|
114
|
+
# The API Client should not retry this and the plugin should consume
|
115
|
+
# the exception.
|
116
|
+
root_error_code = PARSE_ERROR_RESPONSE_BODY['error']['code']
|
117
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI)
|
118
|
+
.to_return(status: root_error_code,
|
119
|
+
body: PARSE_ERROR_RESPONSE_BODY.to_json)
|
120
|
+
d = create_driver(ENABLE_PROMETHEUS_CONFIG)
|
121
|
+
d.emit('message' => log_entry(0))
|
122
|
+
d.run
|
123
|
+
assert_prometheus_metric_value(
|
124
|
+
:stackdriver_successful_requests_count, 0,
|
125
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
126
|
+
grpc: false, code: 200
|
127
|
+
)
|
128
|
+
assert_prometheus_metric_value(
|
129
|
+
:stackdriver_failed_requests_count, 1,
|
130
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
131
|
+
grpc: false, code: 400
|
132
|
+
)
|
133
|
+
assert_prometheus_metric_value(
|
134
|
+
:stackdriver_ingested_entries_count, 0,
|
135
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
136
|
+
grpc: false, code: 200
|
137
|
+
)
|
138
|
+
assert_prometheus_metric_value(
|
139
|
+
:stackdriver_dropped_entries_count, 1,
|
140
|
+
'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
|
141
|
+
grpc: false, code: 400
|
142
|
+
)
|
143
|
+
assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 1)
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_server_error
|
147
|
+
setup_gce_metadata_stubs
|
148
|
+
# The API client should retry this once, then throw an exception which
|
149
|
+
# gets propagated through the plugin.
|
150
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI)
|
151
|
+
.to_return(status: 500, body: 'Server Error')
|
152
|
+
d = create_driver
|
153
|
+
d.emit('message' => log_entry(0))
|
154
|
+
exception_count = 0
|
155
|
+
begin
|
156
|
+
d.run
|
157
|
+
rescue Google::Apis::ServerError => e
|
158
|
+
assert_equal 'Server error', e.message
|
159
|
+
exception_count += 1
|
160
|
+
end
|
161
|
+
assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 1)
|
162
|
+
assert_equal 1, exception_count
|
163
|
+
end
|
164
|
+
|
165
|
+
# This test looks similar between the grpc and non-grpc paths except that when
|
166
|
+
# parsing "105", the grpc path responds with "DEBUG", while the non-grpc path
|
167
|
+
# responds with "100".
|
168
|
+
#
|
169
|
+
# TODO(lingshi) consolidate the tests between the grpc path and the non-grpc
|
170
|
+
# path, or at least split into two tests, one with string severities and one
|
171
|
+
# with numeric severities.
|
172
|
+
def test_severities
|
173
|
+
setup_gce_metadata_stubs
|
174
|
+
expected_severity = []
|
175
|
+
emit_index = 0
|
176
|
+
setup_logging_stubs do
|
177
|
+
d = create_driver
|
178
|
+
# Array of pairs of [parsed_severity, expected_severity]
|
179
|
+
[%w[INFO INFO], %w[warn WARNING], %w[E ERROR], %w[BLAH DEFAULT],
|
180
|
+
['105', 100], ['', 'DEFAULT']].each do |sev|
|
181
|
+
d.emit('message' => log_entry(emit_index), 'severity' => sev[0])
|
182
|
+
expected_severity.push(sev[1])
|
183
|
+
emit_index += 1
|
184
|
+
end
|
185
|
+
d.run
|
186
|
+
end
|
187
|
+
verify_index = 0
|
188
|
+
verify_log_entries(emit_index, COMPUTE_PARAMS) do |entry|
|
189
|
+
assert_equal expected_severity[verify_index],
|
190
|
+
entry['severity'], entry
|
191
|
+
verify_index += 1
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_parse_severity
|
196
|
+
test_obj = Fluent::GoogleCloudOutput.new
|
197
|
+
|
198
|
+
# known severities should translate to themselves, regardless of case
|
199
|
+
%w[DEFAULT DEBUG INFO NOTICE WARNING ERROR CRITICAL ALERT EMERGENCY].each \
|
200
|
+
do |severity|
|
201
|
+
assert_equal(severity, test_obj.parse_severity(severity))
|
202
|
+
assert_equal(severity, test_obj.parse_severity(severity.downcase))
|
203
|
+
assert_equal(severity, test_obj.parse_severity(severity.capitalize))
|
204
|
+
end
|
205
|
+
|
206
|
+
# numeric levels
|
207
|
+
assert_equal(0, test_obj.parse_severity('0'))
|
208
|
+
assert_equal(100, test_obj.parse_severity('100'))
|
209
|
+
assert_equal(200, test_obj.parse_severity('200'))
|
210
|
+
assert_equal(300, test_obj.parse_severity('300'))
|
211
|
+
assert_equal(400, test_obj.parse_severity('400'))
|
212
|
+
assert_equal(500, test_obj.parse_severity('500'))
|
213
|
+
assert_equal(600, test_obj.parse_severity('600'))
|
214
|
+
assert_equal(700, test_obj.parse_severity('700'))
|
215
|
+
assert_equal(800, test_obj.parse_severity('800'))
|
216
|
+
|
217
|
+
assert_equal(800, test_obj.parse_severity('900'))
|
218
|
+
assert_equal(0, test_obj.parse_severity('1'))
|
219
|
+
assert_equal(100, test_obj.parse_severity('105'))
|
220
|
+
assert_equal(400, test_obj.parse_severity('420'))
|
221
|
+
assert_equal(700, test_obj.parse_severity('799'))
|
222
|
+
|
223
|
+
assert_equal(100, test_obj.parse_severity('105 '))
|
224
|
+
assert_equal(100, test_obj.parse_severity(' 105'))
|
225
|
+
assert_equal(100, test_obj.parse_severity(' 105 '))
|
226
|
+
|
227
|
+
assert_equal(100, test_obj.parse_severity(100))
|
228
|
+
|
229
|
+
assert_equal('DEFAULT', test_obj.parse_severity('-100'))
|
230
|
+
assert_equal('DEFAULT', test_obj.parse_severity('105 100'))
|
231
|
+
|
232
|
+
# synonyms for existing log levels
|
233
|
+
assert_equal('ERROR', test_obj.parse_severity('ERR'))
|
234
|
+
assert_equal('ERROR', test_obj.parse_severity('SEVERE'))
|
235
|
+
assert_equal('WARNING', test_obj.parse_severity('WARN'))
|
236
|
+
assert_equal('CRITICAL', test_obj.parse_severity('FATAL'))
|
237
|
+
assert_equal('DEBUG', test_obj.parse_severity('TRACE'))
|
238
|
+
assert_equal('DEBUG', test_obj.parse_severity('TRACE_INT'))
|
239
|
+
assert_equal('DEBUG', test_obj.parse_severity('FINE'))
|
240
|
+
assert_equal('DEBUG', test_obj.parse_severity('FINER'))
|
241
|
+
assert_equal('DEBUG', test_obj.parse_severity('FINEST'))
|
242
|
+
assert_equal('DEBUG', test_obj.parse_severity('CONFIG'))
|
243
|
+
|
244
|
+
# single letters.
|
245
|
+
assert_equal('DEBUG', test_obj.parse_severity('D'))
|
246
|
+
assert_equal('INFO', test_obj.parse_severity('I'))
|
247
|
+
assert_equal('NOTICE', test_obj.parse_severity('N'))
|
248
|
+
assert_equal('WARNING', test_obj.parse_severity('W'))
|
249
|
+
assert_equal('ERROR', test_obj.parse_severity('E'))
|
250
|
+
assert_equal('CRITICAL', test_obj.parse_severity('C'))
|
251
|
+
assert_equal('ALERT', test_obj.parse_severity('A'))
|
252
|
+
assert_equal('ERROR', test_obj.parse_severity('e'))
|
253
|
+
|
254
|
+
assert_equal('DEFAULT', test_obj.parse_severity('x'))
|
255
|
+
assert_equal('DEFAULT', test_obj.parse_severity('-'))
|
256
|
+
|
257
|
+
# leading/trailing whitespace should be stripped
|
258
|
+
assert_equal('ERROR', test_obj.parse_severity(' ERROR'))
|
259
|
+
assert_equal('ERROR', test_obj.parse_severity('ERROR '))
|
260
|
+
assert_equal('ERROR', test_obj.parse_severity(' ERROR '))
|
261
|
+
assert_equal('ERROR', test_obj.parse_severity("\t ERROR "))
|
262
|
+
# space in the middle should not be stripped.
|
263
|
+
assert_equal('DEFAULT', test_obj.parse_severity('ER ROR'))
|
264
|
+
|
265
|
+
# anything else should translate to 'DEFAULT'
|
266
|
+
assert_equal('DEFAULT', test_obj.parse_severity(nil))
|
267
|
+
assert_equal('DEFAULT', test_obj.parse_severity(Object.new))
|
268
|
+
assert_equal('DEFAULT', test_obj.parse_severity({}))
|
269
|
+
assert_equal('DEFAULT', test_obj.parse_severity([]))
|
270
|
+
assert_equal('DEFAULT', test_obj.parse_severity(100.0))
|
271
|
+
assert_equal('DEFAULT', test_obj.parse_severity(''))
|
272
|
+
assert_equal('DEFAULT', test_obj.parse_severity('garbage'))
|
273
|
+
assert_equal('DEFAULT', test_obj.parse_severity('er'))
|
274
|
+
end
|
275
|
+
|
276
|
+
def test_non_integer_timestamp
|
277
|
+
setup_gce_metadata_stubs
|
278
|
+
time = Time.now
|
279
|
+
[
|
280
|
+
{ 'seconds' => nil, 'nanos' => nil },
|
281
|
+
{ 'seconds' => nil, 'nanos' => time.tv_nsec },
|
282
|
+
{ 'seconds' => 'seconds', 'nanos' => time.tv_nsec },
|
283
|
+
{ 'seconds' => time.tv_sec, 'nanos' => 'nanos' },
|
284
|
+
{ 'seconds' => time.tv_sec, 'nanos' => nil }
|
285
|
+
].each do |timestamp|
|
286
|
+
setup_logging_stubs do
|
287
|
+
d = create_driver
|
288
|
+
@logs_sent = []
|
289
|
+
d.emit('message' => log_entry(0), 'timestamp' => timestamp)
|
290
|
+
d.run
|
291
|
+
end
|
292
|
+
verify_log_entries(1, COMPUTE_PARAMS) do |entry|
|
293
|
+
assert_equal timestamp, entry['timestamp'], 'Test with timestamp ' \
|
294
|
+
"'#{timestamp}' failed for entry: '#{entry}'."
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_statusz_endpoint
|
300
|
+
setup_gce_metadata_stubs
|
301
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
302
|
+
# TODO(davidbtucker): Consider searching for an unused port
|
303
|
+
# instead of hardcoding a constant here.
|
304
|
+
d = create_driver(CONFIG_STATUSZ)
|
305
|
+
d.run do
|
306
|
+
resp = Net::HTTP.get('127.0.0.1', '/statusz', 5678)
|
307
|
+
must_match = [
|
308
|
+
'<h1>Status for .*</h1>.*',
|
309
|
+
'\bStarted: .*<br>',
|
310
|
+
'\bUp \d+ hr \d{2} min \d{2} sec<br>',
|
311
|
+
|
312
|
+
'\badjust_invalid_timestamps\b.*\bfalse\b',
|
313
|
+
'\bautoformat_stackdriver_trace\b.*\bfalse\b',
|
314
|
+
'\bcoerce_to_utf8\b.*\bfalse\b',
|
315
|
+
'\bdetect_json\b.*\btrue\b',
|
316
|
+
'\bdetect_subservice\b.*\bfalse\b',
|
317
|
+
'\benable_monitoring\b.*\btrue\b',
|
318
|
+
'\bhttp_request_key\b.*\btest_http_request_key\b',
|
319
|
+
'\binsert_id_key\b.*\btest_insert_id_key\b',
|
320
|
+
'\bk8s_cluster_location\b.*\btest-k8s-cluster-location\b',
|
321
|
+
'\bk8s_cluster_name\b.*\btest-k8s-cluster-name\b',
|
322
|
+
'\bkubernetes_tag_regexp\b.*\b.*test-regexp.*\b',
|
323
|
+
'\blabel_map\b.*{"label_map_key"=>"label_map_value"}',
|
324
|
+
'\blabels_key\b.*\btest_labels_key\b',
|
325
|
+
'\blabels\b.*{"labels_key"=>"labels_value"}',
|
326
|
+
'\blogging_api_url\b.*\bhttp://localhost:52000\b',
|
327
|
+
'\bmonitoring_type\b.*\bnot_prometheus\b',
|
328
|
+
'\bnon_utf8_replacement_string\b.*\bzzz\b',
|
329
|
+
'\boperation_key\b.*\btest_operation_key\b',
|
330
|
+
'\bproject_id\b.*\btest-project-id-123\b',
|
331
|
+
'\brequire_valid_tags\b.*\btrue\b',
|
332
|
+
'\bsource_location_key\b.*\btest_source_location_key\b',
|
333
|
+
'\bspan_id_key\b.*\btest_span_id_key\b',
|
334
|
+
'\bsplit_logs_by_tag\b.*\btrue\b',
|
335
|
+
'\bstatusz_port\b.*\b5678\b',
|
336
|
+
'\bsubservice_name\b.*\btest_subservice_name\b',
|
337
|
+
'\btrace_key\b.*\btest_trace_key\b',
|
338
|
+
'\btrace_sampled_key\b.*\btest_trace_sampled_key\b',
|
339
|
+
'\buse_aws_availability_zone\b.*\bfalse\b',
|
340
|
+
'\buse_grpc\b.*\btrue\b',
|
341
|
+
'\buse_metadata_service\b.*\bfalse\b',
|
342
|
+
'\bvm_id\b.*\b12345\b',
|
343
|
+
'\bvm_name\b.*\btest.hostname.org\b',
|
344
|
+
'\bzone\b.*\basia-east2\b',
|
345
|
+
|
346
|
+
'^</html>$'
|
347
|
+
]
|
348
|
+
must_match.each do |re|
|
349
|
+
assert_match Regexp.new(re), resp
|
350
|
+
end
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
private
|
355
|
+
|
356
|
+
WRITE_LOG_ENTRIES_URI =
|
357
|
+
'https://logging.googleapis.com/v2/entries:write'.freeze
|
358
|
+
|
359
|
+
def rename_key(hash, old_key, new_key)
|
360
|
+
hash.merge(new_key => hash[old_key]).reject { |k, _| k == old_key }
|
361
|
+
end
|
362
|
+
|
363
|
+
# Set up http stubs to mock the external calls.
|
364
|
+
def setup_logging_stubs(_error = nil, code = nil, message = 'some message')
|
365
|
+
stub_request(:post, WRITE_LOG_ENTRIES_URI).to_return do |request|
|
366
|
+
@logs_sent << JSON.parse(request.body)
|
367
|
+
{ status: code, body: message }
|
368
|
+
end
|
369
|
+
yield
|
370
|
+
end
|
371
|
+
|
372
|
+
# Whether this is the grpc path
|
373
|
+
def use_grpc
|
374
|
+
false
|
375
|
+
end
|
376
|
+
|
377
|
+
# The OK status code for the grpc path.
|
378
|
+
def ok_status_code
|
379
|
+
200
|
380
|
+
end
|
381
|
+
|
382
|
+
# A client side error status code for the grpc path.
|
383
|
+
def client_error_status_code
|
384
|
+
401
|
385
|
+
end
|
386
|
+
|
387
|
+
# A server side error status code for the grpc path.
|
388
|
+
def server_error_status_code
|
389
|
+
500
|
390
|
+
end
|
391
|
+
|
392
|
+
# The parent error type to expect in the mock
|
393
|
+
def mock_error_type
|
394
|
+
Google::Apis::Error
|
395
|
+
end
|
396
|
+
|
397
|
+
# The conversions from user input to output.
|
398
|
+
def latency_conversion
|
399
|
+
{
|
400
|
+
'32 s' => { 'seconds' => 32 },
|
401
|
+
'32s' => { 'seconds' => 32 },
|
402
|
+
'0.32s' => { 'nanos' => 320_000_000 },
|
403
|
+
' 123 s ' => { 'seconds' => 123 },
|
404
|
+
'1.3442 s' => { 'seconds' => 1, 'nanos' => 344_200_000 },
|
405
|
+
|
406
|
+
# Test whitespace.
|
407
|
+
# \t: tab. \r: carriage return. \n: line break.
|
408
|
+
# \v: vertical whitespace. \f: form feed.
|
409
|
+
"\t123.5\ts\t" => { 'seconds' => 123, 'nanos' => 500_000_000 },
|
410
|
+
"\r123.5\rs\r" => { 'seconds' => 123, 'nanos' => 500_000_000 },
|
411
|
+
"\n123.5\ns\n" => { 'seconds' => 123, 'nanos' => 500_000_000 },
|
412
|
+
"\v123.5\vs\v" => { 'seconds' => 123, 'nanos' => 500_000_000 },
|
413
|
+
"\f123.5\fs\f" => { 'seconds' => 123, 'nanos' => 500_000_000 },
|
414
|
+
"\r123.5\ts\f" => { 'seconds' => 123, 'nanos' => 500_000_000 }
|
415
|
+
}
|
416
|
+
end
|
417
|
+
|
418
|
+
# Create a Fluentd output test driver with the Google Cloud Output plugin.
|
419
|
+
def create_driver(conf = APPLICATION_DEFAULT_CONFIG,
|
420
|
+
tag = 'test',
|
421
|
+
multi_tags = false)
|
422
|
+
driver = if multi_tags
|
423
|
+
Fluent::Test::MultiTagBufferedOutputTestDriver.new(
|
424
|
+
Fluent::GoogleCloudOutput
|
425
|
+
)
|
426
|
+
else
|
427
|
+
Fluent::Test::BufferedOutputTestDriver.new(
|
428
|
+
Fluent::GoogleCloudOutput, tag
|
429
|
+
)
|
430
|
+
end
|
431
|
+
driver.configure(conf, true)
|
432
|
+
end
|
433
|
+
|
434
|
+
# Verify the number and the content of the log entries match the expectation.
|
435
|
+
# The caller can optionally provide a block which is called for each entry.
|
436
|
+
def verify_log_entries(expected_count, params, payload_type = 'textPayload',
|
437
|
+
check_exact_entry_labels = true, &block)
|
438
|
+
verify_json_log_entries(expected_count, params, payload_type,
|
439
|
+
check_exact_entry_labels, &block)
|
440
|
+
end
|
441
|
+
|
442
|
+
# For an optional field with default values, Protobuf omits the field when it
|
443
|
+
# is deserialized to json. So we need to add an extra check for gRPC which
|
444
|
+
# uses Protobuf.
|
445
|
+
#
|
446
|
+
# An optional block can be passed in if we need to assert something other than
|
447
|
+
# a plain equal. e.g. assert_in_delta.
|
448
|
+
def assert_equal_with_default(field, expected_value, _default_value, entry)
|
449
|
+
if block_given?
|
450
|
+
yield
|
451
|
+
else
|
452
|
+
assert_equal expected_value, field, entry
|
453
|
+
end
|
454
|
+
end
|
455
|
+
|
456
|
+
def expected_operation_message2
|
457
|
+
OPERATION_MESSAGE2
|
458
|
+
end
|
459
|
+
|
460
|
+
# Directly return the timestamp value, which should be a hash two keys:
|
461
|
+
# "seconds" and "nanos".
|
462
|
+
def timestamp_parse(timestamp)
|
463
|
+
timestamp
|
464
|
+
end
|
465
|
+
end
|