fluent-plugin-vadimberezniker-gcp 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,478 @@
1
+ # Copyright 2016 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 'grpc'
16
+
17
+ require_relative 'base_test'
18
+ require_relative 'test_driver'
19
+
20
+ # Unit tests for Google Cloud Logging plugin
21
+ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
22
+ include BaseTest
23
+
24
+ def test_configure_use_grpc
25
+ setup_gce_metadata_stubs
26
+ d = create_driver
27
+ assert_true d.instance.instance_variable_get(:@use_grpc)
28
+ end
29
+
30
+ def test_user_agent
31
+ setup_gce_metadata_stubs
32
+
33
+ user_agent = nil
34
+ # Record user agent when creating a GRPC::Core::Channel.
35
+ GRPC::Core::Channel.class_eval do
36
+ old_initialize = instance_method(:initialize)
37
+ # Suppress redefine warning (https://bugs.ruby-lang.org/issues/17055).
38
+ alias_method :initialize, :initialize
39
+ define_method(:initialize) do |url, args, creds|
40
+ user_agent = args['grpc.primary_user_agent']
41
+ old_initialize.bind(self).call(url, args, creds)
42
+ end
43
+ end
44
+
45
+ d = create_driver
46
+ d.instance.send :init_api_client
47
+ assert_match Regexp.new("#{Fluent::GoogleCloudOutput::PLUGIN_NAME}/" \
48
+ "#{Fluent::GoogleCloudOutput::PLUGIN_VERSION}"), \
49
+ user_agent
50
+ end
51
+
52
+ def test_client_error
53
+ setup_gce_metadata_stubs
54
+ {
55
+ GRPC::Core::StatusCodes::CANCELLED => 'Cancelled',
56
+ GRPC::Core::StatusCodes::UNKNOWN => 'Unknown',
57
+ GRPC::Core::StatusCodes::INVALID_ARGUMENT => 'InvalidArgument',
58
+ GRPC::Core::StatusCodes::NOT_FOUND => 'NotFound',
59
+ GRPC::Core::StatusCodes::PERMISSION_DENIED => 'PermissionDenied',
60
+ GRPC::Core::StatusCodes::RESOURCE_EXHAUSTED => 'ResourceExhausted',
61
+ GRPC::Core::StatusCodes::FAILED_PRECONDITION => 'FailedPrecondition',
62
+ GRPC::Core::StatusCodes::ABORTED => 'Aborted',
63
+ GRPC::Core::StatusCodes::UNAUTHENTICATED => 'Unauthenticated'
64
+ }.each_with_index do |(code, message), index|
65
+ setup_logging_stubs(nil, code, message) do
66
+ d = create_driver(USE_GRPC_CONFIG, 'test')
67
+ # The API Client should not retry this and the plugin should consume the
68
+ # exception.
69
+ d.emit('message' => log_entry(0))
70
+ d.run
71
+ end
72
+ assert_equal 1, @failed_attempts.size, "Index #{index} failed."
73
+ end
74
+ end
75
+
76
+ def test_invalid_error
77
+ setup_gce_metadata_stubs
78
+ setup_logging_stubs(RuntimeError.new('Some non-gRPC error')) do
79
+ d = create_driver(USE_GRPC_CONFIG, 'test')
80
+ # The API Client should not retry this and the plugin should consume the
81
+ # exception.
82
+ d.emit('message' => log_entry(0))
83
+ d.run
84
+ end
85
+ assert_equal 1, @failed_attempts.size
86
+ end
87
+
88
+ def test_partial_success
89
+ setup_gce_metadata_stubs
90
+ clear_metrics
91
+ setup_logging_stubs(
92
+ GRPC::PermissionDenied.new('User not authorized.',
93
+ PARTIAL_SUCCESS_GRPC_METADATA)
94
+ ) do
95
+ # The API Client should not retry this and the plugin should consume
96
+ # the exception.
97
+ d = create_driver(ENABLE_PROMETHEUS_CONFIG)
98
+ 4.times do |i|
99
+ d.emit('message' => log_entry(i.to_s))
100
+ end
101
+ d.run
102
+ assert_prometheus_metric_value(
103
+ :stackdriver_successful_requests_count, 1,
104
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
105
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::OK
106
+ )
107
+ assert_prometheus_metric_value(
108
+ :stackdriver_failed_requests_count, 0,
109
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
110
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::PERMISSION_DENIED
111
+ )
112
+ assert_prometheus_metric_value(
113
+ :stackdriver_ingested_entries_count, 1,
114
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
115
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::OK
116
+ )
117
+ assert_prometheus_metric_value(
118
+ :stackdriver_dropped_entries_count, 2,
119
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
120
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::INVALID_ARGUMENT
121
+ )
122
+ assert_prometheus_metric_value(
123
+ :stackdriver_dropped_entries_count, 1,
124
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
125
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::PERMISSION_DENIED
126
+ )
127
+ end
128
+ end
129
+
130
+ def test_non_api_error
131
+ setup_gce_metadata_stubs
132
+ clear_metrics
133
+ setup_logging_stubs(
134
+ GRPC::InvalidArgument.new('internal client error',
135
+ PARSE_ERROR_GRPC_METADATA)
136
+ ) do
137
+ # The API Client should not retry this and the plugin should consume
138
+ # the exception.
139
+ d = create_driver(ENABLE_PROMETHEUS_CONFIG)
140
+ d.emit('message' => log_entry(0))
141
+ d.run
142
+ assert_prometheus_metric_value(
143
+ :stackdriver_successful_requests_count, 0,
144
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
145
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::OK
146
+ )
147
+ assert_prometheus_metric_value(
148
+ :stackdriver_failed_requests_count, 1,
149
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
150
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::INVALID_ARGUMENT
151
+ )
152
+ assert_prometheus_metric_value(
153
+ :stackdriver_ingested_entries_count, 0,
154
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
155
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::OK
156
+ )
157
+ assert_prometheus_metric_value(
158
+ :stackdriver_dropped_entries_count, 1,
159
+ 'agent.googleapis.com/agent', OpenCensus::Stats::Aggregation::Sum, d,
160
+ grpc: use_grpc, code: GRPC::Core::StatusCodes::INVALID_ARGUMENT
161
+ )
162
+ end
163
+ end
164
+
165
+ def test_server_error
166
+ setup_gce_metadata_stubs
167
+ {
168
+ GRPC::Core::StatusCodes::DEADLINE_EXCEEDED => 'DeadlineExceeded',
169
+ GRPC::Core::StatusCodes::UNIMPLEMENTED => 'Unimplemented',
170
+ GRPC::Core::StatusCodes::INTERNAL => 'Internal',
171
+ GRPC::Core::StatusCodes::UNAVAILABLE => 'Unavailable'
172
+ }.each_with_index do |(code, message), index|
173
+ exception_count = 0
174
+ setup_logging_stubs(nil, code, message) do
175
+ d = create_driver(USE_GRPC_CONFIG, 'test')
176
+ # The API client should retry this once, then throw an exception which
177
+ # gets propagated through the plugin
178
+ d.emit('message' => log_entry(0))
179
+ begin
180
+ d.run
181
+ rescue GRPC::BadStatus => e
182
+ assert_equal "#{code}:#{message}", e.message
183
+ exception_count += 1
184
+ end
185
+ end
186
+ assert_equal 1, @failed_attempts.size, "Index #{index} failed."
187
+ assert_equal 1, exception_count, "Index #{index} failed."
188
+ end
189
+ end
190
+
191
+ # This test looks similar between the grpc and non-grpc paths except that when
192
+ # parsing "105", the grpc path responds with "DEBUG", while the non-grpc path
193
+ # responds with "100".
194
+ #
195
+ # TODO(lingshi) consolidate the tests between the grpc path and the non-grpc
196
+ # path, or at least split into two tests, one with string severities and one
197
+ # with numeric severities.
198
+ def test_severities
199
+ setup_gce_metadata_stubs
200
+ expected_severity = []
201
+ emit_index = 0
202
+ setup_logging_stubs do
203
+ d = create_driver
204
+ # Array of pairs of [parsed_severity, expected_severity]
205
+ [%w[INFO INFO], %w[warn WARNING], %w[E ERROR], %w[BLAH DEFAULT],
206
+ %w[105 DEBUG], ['', 'DEFAULT']].each do |sev|
207
+ d.emit('message' => log_entry(emit_index), 'severity' => sev[0])
208
+ expected_severity.push(sev[1])
209
+ emit_index += 1
210
+ end
211
+ d.run
212
+ end
213
+ verify_index = 0
214
+ verify_log_entries(emit_index, COMPUTE_PARAMS) do |entry|
215
+ assert_equal_with_default(entry['severity'],
216
+ expected_severity[verify_index],
217
+ 'DEFAULT', entry)
218
+ verify_index += 1
219
+ end
220
+ end
221
+
222
+ # TODO(qingling128): Verify if we need this on the REST side and add it if
223
+ # needed.
224
+ def test_struct_payload_non_utf8_log
225
+ setup_gce_metadata_stubs
226
+ setup_logging_stubs do
227
+ d = create_driver
228
+ d.emit('msg' => log_entry(0),
229
+ 'normal_key' => "test#{non_utf8_character}non utf8",
230
+ "non_utf8#{non_utf8_character}key" => 5000,
231
+ 'nested_struct' => { "non_utf8#{non_utf8_character}key" => \
232
+ "test#{non_utf8_character}non utf8" },
233
+ 'null_field' => nil)
234
+ d.run
235
+ end
236
+ verify_log_entries(1, COMPUTE_PARAMS, 'jsonPayload') do |entry|
237
+ fields = entry['jsonPayload']
238
+ assert_equal 5, fields.size, entry
239
+ assert_equal 'test log entry 0', fields['msg'], entry
240
+ assert_equal 'test non utf8', fields['normal_key'], entry
241
+ assert_equal 5000, fields['non_utf8 key'], entry
242
+ assert_equal 'test non utf8', fields['nested_struct']['non_utf8 key'],
243
+ entry
244
+ assert_nil fields['null_field'], entry
245
+ end
246
+ end
247
+
248
+ def test_non_integer_timestamp
249
+ setup_gce_metadata_stubs
250
+ time = Time.now
251
+ {
252
+ { 'seconds' => nil, 'nanos' => nil } => nil,
253
+ { 'seconds' => nil, 'nanos' => time.tv_nsec } => nil,
254
+ { 'seconds' => 'seconds', 'nanos' => time.tv_nsec } => nil,
255
+ { 'seconds' => time.tv_sec, 'nanos' => 'nanos' } => \
256
+ time.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
257
+ { 'seconds' => time.tv_sec, 'nanos' => nil } => \
258
+ time.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
259
+ }.each do |input, expected|
260
+ setup_logging_stubs do
261
+ d = create_driver
262
+ @logs_sent = []
263
+ d.emit('message' => log_entry(0), 'timestamp' => input)
264
+ d.run
265
+ end
266
+ verify_log_entries(1, COMPUTE_PARAMS) do |entry|
267
+ assert_equal expected, entry['timestamp'], 'Test with timestamp ' \
268
+ "'#{input}' failed for entry: '#{entry}'."
269
+ end
270
+ end
271
+ end
272
+
273
+ private
274
+
275
+ WriteLogEntriesRequest = Google::Cloud::Logging::V2::WriteLogEntriesRequest
276
+ WriteLogEntriesResponse = Google::Cloud::Logging::V2::WriteLogEntriesResponse
277
+
278
+ USE_GRPC_CONFIG = %(
279
+ use_grpc true
280
+ ).freeze
281
+
282
+ # The conversions from user input to output.
283
+ def latency_conversion
284
+ {
285
+ '32 s' => '32s',
286
+ '32s' => '32s',
287
+ '0.32s' => '0.320s',
288
+ ' 123 s ' => '123s',
289
+ '1.3442 s' => '1.344200s',
290
+
291
+ # Test whitespace.
292
+ # \t: tab. \r: carriage return. \n: line break.
293
+ # \v: vertical whitespace. \f: form feed.
294
+ "\t123.5\ts\t" => '123.500s',
295
+ "\r123.5\rs\r" => '123.500s',
296
+ "\n123.5\ns\n" => '123.500s',
297
+ "\v123.5\vs\v" => '123.500s',
298
+ "\f123.5\fs\f" => '123.500s',
299
+ "\r123.5\ts\f" => '123.500s'
300
+ }
301
+ end
302
+
303
+ # Create a Fluentd output test driver with the Google Cloud Output plugin with
304
+ # grpc enabled. The signature of this method is different between the grpc
305
+ # path and the non-grpc path. For grpc, an additional grpc stub class can be
306
+ # passed in to construct the mock used by the test driver.
307
+ def create_driver(conf = APPLICATION_DEFAULT_CONFIG,
308
+ tag = 'test',
309
+ multi_tags = false)
310
+ conf += USE_GRPC_CONFIG
311
+ driver = if multi_tags
312
+ Fluent::Test::MultiTagBufferedOutputTestDriver.new(
313
+ GoogleCloudOutputWithGRPCMock.new(@grpc_stub)
314
+ )
315
+ else
316
+ Fluent::Test::BufferedOutputTestDriver.new(
317
+ GoogleCloudOutputWithGRPCMock.new(@grpc_stub), tag
318
+ )
319
+ end
320
+ driver.configure(conf, true)
321
+ end
322
+
323
+ # Google Cloud Fluent output stub with grpc mock.
324
+ class GoogleCloudOutputWithGRPCMock < Fluent::GoogleCloudOutput
325
+ def initialize(grpc_stub)
326
+ super()
327
+ @grpc_stub = grpc_stub
328
+ end
329
+
330
+ def api_client
331
+ @grpc_stub
332
+ end
333
+ end
334
+
335
+ # GRPC logging mock that successfully logs the records.
336
+ class GRPCLoggingMockService <
337
+ Google::Cloud::Logging::V2::LoggingService::Client
338
+ def initialize(requests_received)
339
+ super()
340
+ @requests_received = requests_received
341
+ end
342
+
343
+ def write_log_entries(entries:,
344
+ log_name: nil,
345
+ resource: nil,
346
+ labels: nil,
347
+ partial_success: nil)
348
+ request = Google::Apis::LoggingV2::WriteLogEntriesRequest.new(
349
+ log_name: log_name,
350
+ resource: resource,
351
+ labels: labels,
352
+ entries: entries,
353
+ partial_success: partial_success
354
+ )
355
+ @requests_received << request
356
+ WriteLogEntriesResponse.new
357
+ end
358
+ end
359
+
360
+ # GRPC logging mock that fails and returns server side or client side errors.
361
+ class GRPCLoggingMockFailingService <
362
+ Google::Cloud::Logging::V2::LoggingService::Client
363
+ def initialize(error, failed_attempts)
364
+ super()
365
+ @error = error
366
+ @failed_attempts = failed_attempts
367
+ end
368
+
369
+ # rubocop:disable Lint/UnusedMethodArgument
370
+ def write_log_entries(entries:,
371
+ log_name: nil,
372
+ resource: nil,
373
+ labels: nil,
374
+ partial_success: nil)
375
+ @failed_attempts << 1
376
+ begin
377
+ raise @error
378
+ rescue StandardError
379
+ # Google::Cloud::Error will wrap the latest thrown exception as @cause.
380
+ raise Google::Cloud::Error, 'This test message does not matter.'
381
+ end
382
+ end
383
+ # rubocop:enable Lint/UnusedMethodArgument
384
+ end
385
+
386
+ # Set up grpc stubs to mock the external calls.
387
+ def setup_logging_stubs(error = nil, code = nil, message = 'some message')
388
+ if error.nil? && (code.nil? || code.zero?)
389
+ @requests_sent = []
390
+ @grpc_stub = GRPCLoggingMockService.new(@requests_sent)
391
+ else
392
+ @failed_attempts = []
393
+ # Only fall back to constructing an error with code and message if no
394
+ # error is passed in.
395
+ error ||= GRPC::BadStatus.new_status_exception(code, message)
396
+ @grpc_stub = GRPCLoggingMockFailingService.new(error, @failed_attempts)
397
+ end
398
+ yield
399
+ end
400
+
401
+ # Whether this is the grpc path
402
+ def use_grpc
403
+ true
404
+ end
405
+
406
+ # The OK status code for the grpc path.
407
+ def ok_status_code
408
+ 0
409
+ end
410
+
411
+ # A client side error status code for the grpc path.
412
+ def client_error_status_code
413
+ 16
414
+ end
415
+
416
+ # A server side error status code for the grpc path.
417
+ def server_error_status_code
418
+ 13
419
+ end
420
+
421
+ # The parent error type to expect in the mock
422
+ def mock_error_type
423
+ GRPC::BadStatus
424
+ end
425
+
426
+ # Verify the number and the content of the log entries match the expectation.
427
+ # The caller can optionally provide a block which is called for each entry.
428
+ def verify_log_entries(expected_count, params, payload_type = 'textPayload',
429
+ check_exact_entry_labels = true, &block)
430
+ @requests_sent.each do |request|
431
+ @logs_sent << {
432
+ 'entries' => request.entries.map { |entry| JSON.parse(entry.to_json) },
433
+ 'labels' => request.labels,
434
+ 'resource' => request.resource,
435
+ 'logName' => request.log_name
436
+ }
437
+ end
438
+ verify_json_log_entries(expected_count, params, payload_type,
439
+ check_exact_entry_labels, &block)
440
+ end
441
+
442
+ # Use the right single quotation mark as the sample non-utf8 character.
443
+ def non_utf8_character
444
+ [0x92].pack('C*')
445
+ end
446
+
447
+ # For an optional field with default values, Protobuf omits the field when it
448
+ # is deserialized to json. So we need to add an extra check for gRPC which
449
+ # uses Protobuf.
450
+ #
451
+ # An optional block can be passed in if we need to assert something other than
452
+ # a plain equal. e.g. assert_in_delta.
453
+ def assert_equal_with_default(field, expected_value, default_value, entry)
454
+ if expected_value == default_value
455
+ assert_nil field
456
+ elsif block_given?
457
+ yield
458
+ else
459
+ assert_equal expected_value, field, entry
460
+ end
461
+ end
462
+
463
+ def expected_operation_message2
464
+ # 'last' is a boolean field with false as default value. Protobuf omit
465
+ # fields with default values during deserialization.
466
+ OPERATION_MESSAGE2.reject { |k, _| k == 'last' }
467
+ end
468
+
469
+ # Parse timestamp and convert it to a hash with two keys:
470
+ # "seconds" and "nanos".
471
+ def timestamp_parse(timestamp)
472
+ parsed = Time.parse(timestamp)
473
+ {
474
+ 'seconds' => parsed.tv_sec,
475
+ 'nanos' => parsed.tv_nsec
476
+ }
477
+ end
478
+ end
@@ -0,0 +1,148 @@
1
+ # Copyright 2020 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_relative 'constants'
16
+
17
+ require 'prometheus/client'
18
+ require 'webmock/test_unit'
19
+
20
+ module Utils
21
+ include Constants
22
+
23
+ def delete_env_vars
24
+ # delete environment variables that googleauth uses to find credentials.
25
+ ENV.delete(CREDENTIALS_PATH_ENV_VAR)
26
+ # service account env.
27
+ ENV.delete(PRIVATE_KEY_VAR)
28
+ ENV.delete(CLIENT_EMAIL_VAR)
29
+ ENV.delete(PROJECT_ID_VAR)
30
+ # authorized_user env.
31
+ ENV.delete(CLIENT_ID_VAR)
32
+ ENV.delete(CLIENT_SECRET_VAR)
33
+ ENV.delete(REFRESH_TOKEN_VAR)
34
+ # home var, which is used to find $HOME/.gcloud/...
35
+ ENV.delete('HOME')
36
+ end
37
+
38
+ def stub_metadata_request(metadata_path, response_body)
39
+ stub_request(:get, "http://169.254.169.254/computeMetadata/v1/#{metadata_path}")
40
+ .to_return(body: response_body, status: 200,
41
+ headers: { 'Content-Length' => response_body.length })
42
+ end
43
+
44
+ def setup_no_metadata_service_stubs
45
+ # Simulate a machine with no metadata service present
46
+ stub_request(:any, %r{http://169.254.169.254/.*})
47
+ .to_raise(Errno::EHOSTUNREACH)
48
+ end
49
+
50
+ def setup_gce_metadata_stubs
51
+ # Stub the root, used for platform detection by the plugin and 'googleauth'.
52
+ stub_request(:get, 'http://169.254.169.254')
53
+ .to_return(status: 200, headers: { 'Metadata-Flavor' => 'Google' })
54
+
55
+ # Create stubs for all the GCE metadata lookups the agent needs to make.
56
+ stub_metadata_request('project/project-id', PROJECT_ID)
57
+ stub_metadata_request('instance/zone', FULLY_QUALIFIED_ZONE)
58
+ stub_metadata_request('instance/id', VM_ID)
59
+ stub_metadata_request('instance/attributes/',
60
+ "attribute1\nattribute2\nattribute3")
61
+
62
+ # Used by 'googleauth' to fetch the default service account credentials.
63
+ stub_request(:get, %r{http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/token(?:\?.*)})
64
+ .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
65
+ status: 200,
66
+ headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
67
+ 'Content-Type' => 'application/json' })
68
+ end
69
+
70
+ def setup_ec2_metadata_stubs
71
+ # Stub the root, used for platform detection.
72
+ stub_request(:get, 'http://169.254.169.254')
73
+ .to_return(status: 200, headers: { 'Server' => 'EC2ws' })
74
+
75
+ # Stub the identity document lookup made by the agent.
76
+ stub_request(:get, 'http://169.254.169.254/latest/dynamic/' \
77
+ 'instance-identity/document')
78
+ .to_return(body: EC2_IDENTITY_DOCUMENT, status: 200,
79
+ headers: { 'Content-Length' => EC2_IDENTITY_DOCUMENT.length })
80
+ end
81
+
82
+ def setup_auth_stubs(base_url)
83
+ # Used when loading credentials from a JSON file.
84
+ stub_request(:post, base_url)
85
+ .with(body: hash_including(grant_type: AUTH_GRANT_TYPE))
86
+ .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
87
+ status: 200,
88
+ headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
89
+ 'Content-Type' => 'application/json' })
90
+
91
+ stub_request(:post, base_url)
92
+ .with(body: hash_including(grant_type: 'refresh_token'))
93
+ .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
94
+ status: 200,
95
+ headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
96
+ 'Content-Type' => 'application/json' })
97
+ end
98
+
99
+ def setup_managed_vm_metadata_stubs
100
+ stub_metadata_request(
101
+ 'instance/attributes/',
102
+ "attribute1\ngae_backend_name\ngae_backend_version\nlast_attribute"
103
+ )
104
+ stub_metadata_request('instance/attributes/gae_backend_name',
105
+ MANAGED_VM_BACKEND_NAME)
106
+ stub_metadata_request('instance/attributes/gae_backend_version',
107
+ MANAGED_VM_BACKEND_VERSION)
108
+ end
109
+
110
+ def setup_k8s_metadata_stubs(should_respond = true)
111
+ if should_respond
112
+ stub_metadata_request(
113
+ 'instance/attributes/',
114
+ "attribute1\ncluster-location\ncluster-name\nlast_attribute"
115
+ )
116
+ stub_metadata_request('instance/attributes/cluster-location',
117
+ K8S_LOCATION2)
118
+ stub_metadata_request('instance/attributes/cluster-name',
119
+ K8S_CLUSTER_NAME)
120
+ else
121
+ %w[cluster-location cluster-name].each do |metadata_name|
122
+ stub_request(:get, %r{.*instance/attributes/#{metadata_name}.*})
123
+ .to_return(status: 404,
124
+ body: 'The requested URL /computeMetadata/v1/instance/' \
125
+ "attributes/#{metadata_name} was not found on this" \
126
+ ' server.')
127
+ end
128
+ end
129
+ end
130
+
131
+ def setup_dataproc_metadata_stubs
132
+ stub_metadata_request(
133
+ 'instance/attributes/',
134
+ "attribute1\ndataproc-cluster-uuid\ndataproc-cluster-name"
135
+ )
136
+ stub_metadata_request('instance/attributes/dataproc-cluster-name',
137
+ DATAPROC_CLUSTER_NAME)
138
+ stub_metadata_request('instance/attributes/dataproc-cluster-uuid',
139
+ DATAPROC_CLUSTER_UUID)
140
+ stub_metadata_request('instance/attributes/dataproc-region',
141
+ DATAPROC_REGION)
142
+ end
143
+
144
+ def clear_metrics
145
+ Prometheus::Client.registry.instance_variable_set('@metrics', {})
146
+ OpenCensus::Stats.ensure_recorder.clear_stats
147
+ end
148
+ end