fluent-plugin-google-cloud 0.7.15 → 0.7.16

Sign up to get free protection for your applications and to get access to all the features.
@@ -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