fluent-plugin-google-cloud 0.7.15 → 0.7.16

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.
@@ -464,6 +464,53 @@ module Constants
464
464
  #{CONFIG_LABEL_MAP_CONFLICTING}
465
465
  ).freeze
466
466
 
467
+ # For monitoring config.
468
+ CONFIG_UNKNOWN_MONITORING_TYPE = %(
469
+ enable_monitoring true
470
+ monitoring_type not_prometheus
471
+ ).freeze
472
+
473
+ # For statusz.
474
+ CONFIG_STATUSZ = %(
475
+ statusz_port 5678
476
+
477
+ adjust_invalid_timestamps false
478
+ autoformat_stackdriver_trace false
479
+ coerce_to_utf8 false
480
+ detect_json true
481
+ detect_subservice false
482
+ enable_metadata_agent true
483
+ enable_monitoring true
484
+ http_request_key test_http_request_key
485
+ insert_id_key test_insert_id_key
486
+ k8s_cluster_location test-k8s-cluster-location
487
+ k8s_cluster_name test-k8s-cluster-name
488
+ kubernetes_tag_regexp .*test-regexp.*
489
+ label_map { "label_map_key": "label_map_value" }
490
+ labels_key test_labels_key
491
+ labels { "labels_key": "labels_value" }
492
+ logging_api_url http://localhost:52000
493
+ metadata_agent_url http://localhost:12345
494
+ monitoring_type not_prometheus
495
+ non_utf8_replacement_string zzz
496
+ operation_key test_operation_key
497
+ partial_success false
498
+ project_id test-project-id-123
499
+ require_valid_tags true
500
+ source_location_key test_source_location_key
501
+ span_id_key test_span_id_key
502
+ split_logs_by_tag true
503
+ subservice_name test_subservice_name
504
+ trace_key test_trace_key
505
+ trace_sampled_key test_trace_sampled_key
506
+ use_aws_availability_zone false
507
+ use_grpc true
508
+ use_metadata_service false
509
+ vm_id 12345
510
+ vm_name test.hostname.org
511
+ zone asia-east2
512
+ ).freeze
513
+
467
514
  # Service configurations for various services.
468
515
 
469
516
  # GCE.
@@ -798,16 +845,16 @@ module Constants
798
845
  ).freeze
799
846
 
800
847
  HTTP_REQUEST_MESSAGE = {
801
- 'cacheFillBytes' => 6653,
848
+ 'cacheFillBytes' => '6653',
802
849
  'cacheHit' => true,
803
850
  'cacheLookup' => true,
804
851
  'cacheValidatedWithOriginServer' => true,
805
852
  'protocol' => 'HTTP/1.1',
806
853
  'referer' => 'http://referer/',
807
854
  'remoteIp' => '55.55.55.55',
808
- 'responseSize' => 65,
855
+ 'responseSize' => '65',
809
856
  'requestMethod' => 'POST',
810
- 'requestSize' => 210,
857
+ 'requestSize' => '210',
811
858
  'requestUrl' => 'http://example/',
812
859
  'serverIp' => '66.66.66.66',
813
860
  'status' => 200,
@@ -817,13 +864,13 @@ module Constants
817
864
  SOURCE_LOCATION_MESSAGE = {
818
865
  'file' => 'source/file',
819
866
  'function' => 'my_function',
820
- 'line' => 18
867
+ 'line' => '18'
821
868
  }.freeze
822
869
 
823
870
  SOURCE_LOCATION_MESSAGE2 = {
824
871
  'file' => 'src/file',
825
872
  'function' => 'my_func',
826
- 'line' => 8
873
+ 'line' => '8'
827
874
  }.freeze
828
875
 
829
876
  OPERATION_MESSAGE = {
@@ -1056,8 +1103,26 @@ module Constants
1056
1103
  }.freeze
1057
1104
  end
1058
1105
 
1106
+ PRESERVED_KEYS_TIMESTAMP_FIELDS = [
1107
+ {
1108
+ 'time' => K8S_TIMESTAMP
1109
+ },
1110
+ {
1111
+ 'timeNanos' => K8S_NANOS
1112
+ },
1113
+ {
1114
+ 'timestamp' => {
1115
+ 'nanos' => K8S_NANOS,
1116
+ 'seconds' => K8S_SECONDS_EPOCH
1117
+ }
1118
+ },
1119
+ {
1120
+ 'timestampNanos' => K8S_NANOS,
1121
+ 'timestampSeconds' => K8S_SECONDS_EPOCH
1122
+ }
1123
+ ].freeze
1124
+
1059
1125
  PRESERVED_KEYS_MAP = {
1060
- 'time' => K8S_TIMESTAMP,
1061
1126
  'severity' => CONTAINER_SEVERITY,
1062
1127
  DEFAULT_HTTP_REQUEST_KEY => HTTP_REQUEST_MESSAGE,
1063
1128
  DEFAULT_INSERT_ID_KEY => INSERT_ID,
@@ -339,10 +339,54 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
339
339
  WebMock.disable_net_connect!(allow_localhost: true)
340
340
  # TODO(davidbtucker): Consider searching for an unused port
341
341
  # instead of hardcoding a constant here.
342
- d = create_driver('statusz_port 5678')
342
+ d = create_driver(CONFIG_STATUSZ)
343
343
  d.run do
344
- assert_match Regexp.new('.*<h1>Status</h1>.*'),
345
- Net::HTTP.get('127.0.0.1', '/statusz', 5678)
344
+ resp = Net::HTTP.get('127.0.0.1', '/statusz', 5678)
345
+ must_match = [
346
+ '<h1>Status for .*</h1>.*',
347
+
348
+ '\badjust_invalid_timestamps\b.*\bfalse\b',
349
+ '\bautoformat_stackdriver_trace\b.*\bfalse\b',
350
+ '\bcoerce_to_utf8\b.*\bfalse\b',
351
+ '\bdetect_json\b.*\btrue\b',
352
+ '\bdetect_subservice\b.*\bfalse\b',
353
+ '\benable_metadata_agent\b.*\btrue\b',
354
+ '\benable_monitoring\b.*\btrue\b',
355
+ '\bhttp_request_key\b.*\btest_http_request_key\b',
356
+ '\binsert_id_key\b.*\btest_insert_id_key\b',
357
+ '\bk8s_cluster_location\b.*\btest-k8s-cluster-location\b',
358
+ '\bk8s_cluster_name\b.*\btest-k8s-cluster-name\b',
359
+ '\bkubernetes_tag_regexp\b.*\b.*test-regexp.*\b',
360
+ '\blabel_map\b.*{"label_map_key"=>"label_map_value"}',
361
+ '\blabels_key\b.*\btest_labels_key\b',
362
+ '\blabels\b.*{"labels_key"=>"labels_value"}',
363
+ '\blogging_api_url\b.*\bhttp://localhost:52000\b',
364
+ '\bmetadata_agent_url\b.*\bhttp://localhost:12345\b',
365
+ '\bmonitoring_type\b.*\bnot_prometheus\b',
366
+ '\bnon_utf8_replacement_string\b.*\bzzz\b',
367
+ '\boperation_key\b.*\btest_operation_key\b',
368
+ '\bpartial_success\b.*\bfalse\b',
369
+ '\bproject_id\b.*\btest-project-id-123\b',
370
+ '\brequire_valid_tags\b.*\btrue\b',
371
+ '\bsource_location_key\b.*\btest_source_location_key\b',
372
+ '\bspan_id_key\b.*\btest_span_id_key\b',
373
+ '\bsplit_logs_by_tag\b.*\btrue\b',
374
+ '\bstatusz_port\b.*\b5678\b',
375
+ '\bsubservice_name\b.*\btest_subservice_name\b',
376
+ '\btrace_key\b.*\btest_trace_key\b',
377
+ '\btrace_sampled_key\b.*\btest_trace_sampled_key\b',
378
+ '\buse_aws_availability_zone\b.*\bfalse\b',
379
+ '\buse_grpc\b.*\btrue\b',
380
+ '\buse_metadata_service\b.*\bfalse\b',
381
+ '\bvm_id\b.*\b12345\b',
382
+ '\bvm_name\b.*\btest.hostname.org\b',
383
+ '\bzone\b.*\basia-east2\b',
384
+
385
+ '^</html>$'
386
+ ]
387
+ must_match.each do |re|
388
+ assert_match Regexp.new(re), resp
389
+ end
346
390
  end
347
391
  end
348
392
 
@@ -364,6 +408,27 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
364
408
  yield
365
409
  end
366
410
 
411
+ # The conversions from user input to output.
412
+ def latency_conversion
413
+ {
414
+ '32 s' => { 'seconds' => 32 },
415
+ '32s' => { 'seconds' => 32 },
416
+ '0.32s' => { 'nanos' => 320_000_000 },
417
+ ' 123 s ' => { 'seconds' => 123 },
418
+ '1.3442 s' => { 'seconds' => 1, 'nanos' => 344_200_000 },
419
+
420
+ # Test whitespace.
421
+ # \t: tab. \r: carriage return. \n: line break.
422
+ # \v: vertical whitespace. \f: form feed.
423
+ "\t123.5\ts\t" => { 'seconds' => 123, 'nanos' => 500_000_000 },
424
+ "\r123.5\rs\r" => { 'seconds' => 123, 'nanos' => 500_000_000 },
425
+ "\n123.5\ns\n" => { 'seconds' => 123, 'nanos' => 500_000_000 },
426
+ "\v123.5\vs\v" => { 'seconds' => 123, 'nanos' => 500_000_000 },
427
+ "\f123.5\fs\f" => { 'seconds' => 123, 'nanos' => 500_000_000 },
428
+ "\r123.5\ts\f" => { 'seconds' => 123, 'nanos' => 500_000_000 }
429
+ }
430
+ end
431
+
367
432
  # Create a Fluentd output test driver with the Google Cloud Output plugin.
368
433
  def create_driver(conf = APPLICATION_DEFAULT_CONFIG,
369
434
  tag = 'test',
@@ -400,69 +465,13 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
400
465
  end
401
466
  end
402
467
 
403
- # Get the fields of the payload.
404
- def get_fields(payload)
405
- payload
406
- end
407
-
408
- # Get the value of a struct field.
409
- def get_struct(field)
410
- field
411
- end
412
-
413
- # Get the value of a string field.
414
- def get_string(field)
415
- field
416
- end
417
-
418
- # Get the value of a number field.
419
- def get_number(field)
420
- field
421
- end
422
-
423
- # The null value.
424
- def null_value
425
- nil
426
- end
427
-
428
- # Convert certain fields to strings for compatibility between gRPC and REST.
429
- # See more details in:
430
- # https://github.com/google/google-api-ruby-client/issues/619.
431
- def convert_subfields_to_strings(full_hash, fields_to_convert)
432
- full_hash.merge(Hash[
433
- fields_to_convert.collect do |field_name|
434
- [field_name, full_hash[field_name].to_s]
435
- end
436
- ])
437
- end
438
-
439
- # 'responseSize', 'requestSize', and 'cacheFillBytes' are Integers in the gRPC
440
- # protos, yet Strings in REST API client libraries.
441
- def http_request_message
442
- convert_subfields_to_strings(
443
- HTTP_REQUEST_MESSAGE, %w(cacheFillBytes responseSize requestSize))
444
- end
445
-
446
- # 'line' is an Integer in the gRPC proto, yet a String in the REST API client.
447
- def source_location_message
448
- convert_subfields_to_strings(
449
- SOURCE_LOCATION_MESSAGE, ['line'])
450
- end
451
-
452
- # 'line' is an Integer in the gRPC proto, yet a String in the REST API client.
453
- def source_location_message2
454
- convert_subfields_to_strings(
455
- SOURCE_LOCATION_MESSAGE2, ['line'])
456
- end
457
-
458
468
  def expected_operation_message2
459
469
  OPERATION_MESSAGE2
460
470
  end
461
471
 
462
- # Both expected and actual are Ruby hashes that represent JSON
463
- # objects.
464
- # This method has a different implementation at the gRPC side.
465
- def assert_hash_equal_json(expected, actual)
466
- assert_equal expected, actual, "expected: #{expected}\nactual: #{actual}"
472
+ # Directly return the timestamp value, which should be a hash two keys:
473
+ # "seconds" and "nanos".
474
+ def timestamp_parse(timestamp)
475
+ timestamp
467
476
  end
468
477
  end
@@ -260,6 +260,8 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
260
260
  end
261
261
  end
262
262
 
263
+ # TODO(qingling128): Verify if we need this on the REST side and add it if
264
+ # needed.
263
265
  def test_struct_payload_non_utf8_log
264
266
  setup_gce_metadata_stubs
265
267
  setup_logging_stubs do
@@ -273,14 +275,14 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
273
275
  d.run
274
276
  end
275
277
  verify_log_entries(1, COMPUTE_PARAMS, 'jsonPayload') do |entry|
276
- fields = get_fields(entry['jsonPayload'])
278
+ fields = entry['jsonPayload']
277
279
  assert_equal 5, fields.size, entry
278
- assert_equal 'test log entry 0', get_string(fields['msg']), entry
279
- assert_equal 'test non utf8', get_string(fields['normal_key']), entry
280
- assert_equal 5000, get_number(fields['non_utf8 key']), entry
281
- assert_equal 'test non utf8', get_string(get_fields(get_struct(fields \
282
- ['nested_struct']))['non_utf8 key']), entry
283
- assert_equal null_value, fields['null_field'], entry
280
+ assert_equal 'test log entry 0', fields['msg'], entry
281
+ assert_equal 'test non utf8', fields['normal_key'], entry
282
+ assert_equal 5000, fields['non_utf8 key'], entry
283
+ assert_equal 'test non utf8', fields['nested_struct']['non_utf8 key'],
284
+ entry
285
+ assert_nil fields['null_field'], entry
284
286
  end
285
287
  end
286
288
 
@@ -292,9 +294,9 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
292
294
  { 'seconds' => nil, 'nanos' => time.tv_nsec } => nil,
293
295
  { 'seconds' => 'seconds', 'nanos' => time.tv_nsec } => nil,
294
296
  { 'seconds' => time.tv_sec, 'nanos' => 'nanos' } => \
295
- { 'seconds' => time.tv_sec },
297
+ time.utc.strftime('%Y-%m-%dT%H:%M:%SZ'),
296
298
  { 'seconds' => time.tv_sec, 'nanos' => nil } => \
297
- { 'seconds' => time.tv_sec }
299
+ time.utc.strftime('%Y-%m-%dT%H:%M:%SZ')
298
300
  }.each do |input, expected|
299
301
  setup_logging_stubs do
300
302
  d = create_driver
@@ -318,6 +320,27 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
318
320
  use_grpc true
319
321
  ).freeze
320
322
 
323
+ # The conversions from user input to output.
324
+ def latency_conversion
325
+ {
326
+ '32 s' => '32s',
327
+ '32s' => '32s',
328
+ '0.32s' => '0.320000000s',
329
+ ' 123 s ' => '123s',
330
+ '1.3442 s' => '1.344200000s',
331
+
332
+ # Test whitespace.
333
+ # \t: tab. \r: carriage return. \n: line break.
334
+ # \v: vertical whitespace. \f: form feed.
335
+ "\t123.5\ts\t" => '123.500000000s',
336
+ "\r123.5\rs\r" => '123.500000000s',
337
+ "\n123.5\ns\n" => '123.500000000s',
338
+ "\v123.5\vs\v" => '123.500000000s',
339
+ "\f123.5\fs\f" => '123.500000000s',
340
+ "\r123.5\ts\f" => '123.500000000s'
341
+ }
342
+ end
343
+
321
344
  # Create a Fluentd output test driver with the Google Cloud Output plugin with
322
345
  # grpc enabled. The signature of this method is different between the grpc
323
346
  # path and the non-grpc path. For grpc, an additional grpc stub class can be
@@ -393,7 +416,7 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
393
416
  raise @error
394
417
  rescue
395
418
  # Google::Gax::GaxError will wrap the latest thrown exception as @cause.
396
- raise Google::Gax::GaxError, @message
419
+ raise Google::Gax::GaxError, 'This test message does not matter.'
397
420
  end
398
421
  end
399
422
  # rubocop:enable Lint/UnusedMethodArgument
@@ -448,99 +471,19 @@ class GoogleCloudOutputGRPCTest < Test::Unit::TestCase
448
471
  end
449
472
  end
450
473
 
451
- # Get the fields of the payload.
452
- def get_fields(payload)
453
- payload['fields']
454
- end
455
-
456
- # Get the value of a struct field.
457
- def get_struct(field)
458
- field['structValue']
459
- end
460
-
461
- # Get the value of a string field.
462
- def get_string(field)
463
- field['stringValue']
464
- end
465
-
466
- # Get the value of a number field.
467
- def get_number(field)
468
- field['numberValue']
469
- end
470
-
471
- def get_bool(field)
472
- field['boolValue']
473
- end
474
-
475
- # The null value.
476
- def null_value
477
- { 'nullValue' => 'NULL_VALUE' }
478
- end
479
-
480
- def http_request_message
481
- HTTP_REQUEST_MESSAGE
482
- end
483
-
484
- def source_location_message
485
- SOURCE_LOCATION_MESSAGE
486
- end
487
-
488
- def source_location_message2
489
- SOURCE_LOCATION_MESSAGE2
490
- end
491
-
492
474
  def expected_operation_message2
493
475
  # 'last' is a boolean field with false as default value. Protobuf omit
494
476
  # fields with default values during deserialization.
495
477
  OPERATION_MESSAGE2.reject { |k, _| k == 'last' }
496
478
  end
497
479
 
498
- # expected: A Ruby hash that represents a JSON object.
499
- # e.g.:
500
- # {
501
- # "file" => "source/file",
502
- # "function" => "my_function",
503
- # "line" => 18
504
- # }
505
- #
506
- # actual: A Ruby hash that represents a Proto object.
507
- # e.g.:
508
- # {
509
- # "structValue" => {
510
- # "fields" => {
511
- # "file" => {
512
- # "stringValue" => "source/file"
513
- # },
514
- # "function" => {
515
- # "stringValue" => "my_function"
516
- # },
517
- # "line" => {
518
- # "numberValue" => 18
519
- # }
520
- # }
521
- # }
522
- # }
523
- # This method has a different implementation at the REST side.
524
- def assert_hash_equal_json(expected, actual)
525
- error_message = "expected: #{expected}\nactual: #{actual}"
526
- assert_true actual.is_a?(Hash),
527
- "Expect the actual value to be a hash. #{error_message}"
528
- if actual.key?('stringValue')
529
- assert_equal expected, get_string(actual), error_message
530
- elsif actual.key?('numberValue')
531
- assert_equal expected, get_number(actual), error_message
532
- elsif actual.key?('boolValue')
533
- assert_equal expected, get_bool(actual), error_message
534
- elsif actual.key?('structValue')
535
- expected_copy = expected.dup
536
- get_fields(get_struct(actual)).each do |field_name, nested_actual|
537
- assert_hash_equal_json expected_copy[field_name], nested_actual
538
- expected_copy.reject! { |k, _| k == field_name }
539
- end
540
- # Make sure all fields are matched.
541
- assert_true expected_copy.empty?
542
- else
543
- assert_true false, "Unsupported proto format. #{error_message}"
544
- end
480
+ # Parse timestamp and convert it to a hash with two keys:
481
+ # "seconds" and "nanos".
482
+ def timestamp_parse(timestamp)
483
+ parsed = Time.parse(timestamp)
484
+ {
485
+ 'seconds' => parsed.tv_sec,
486
+ 'nanos' => parsed.tv_nsec
487
+ }
545
488
  end
546
489
  end