fluent-plugin-google-cloud 0.4.17 → 0.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4caeb3e6a9cec431492945788635d7e99e3a0b16
4
- data.tar.gz: a88c1ce91b2335bac4b4a37f0c5899c864652046
3
+ metadata.gz: 1989b8ce8911575be17949674d1d8dfb137dbeda
4
+ data.tar.gz: f5ac2f7ca47a5c61dfc7a32a9254b6de841a2182
5
5
  SHA512:
6
- metadata.gz: f7130260e115f6547ba0db5782534f9f890655298c37beaafa4d9f254adf1f1489bf26b0a31df225e19dc694d81954d91c60aa12a7ffbd20e0018d3cacb50603
7
- data.tar.gz: f8dda17b58fe377783f29f91f93f71bbb2a53f7dca2c440485288efa684c3fba9b3d51e53948add7b8e991d328858972cfab0aec660f2ece1311eea58e3bfe39
6
+ metadata.gz: 9de731cc6f29055be014d6894f0b4930c7c6b5d836c149789d0ae06fe7e14a09687307485b559b6cf77335770a1f84003945468ed9b863d11c49a079142dad1a
7
+ data.tar.gz: aa07feea223b293e68da01fcf7e35facef3aa3af7ce3a8ba014304479e3521129809f004bb94a342ac6a00a2ee83bb0fe4132a2caae87ecdfcc4691cf6adbb5b
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fluent-plugin-google-cloud (0.4.16)
4
+ fluent-plugin-google-cloud (0.5.0)
5
5
  fluentd (~> 0.10)
6
6
  google-api-client (> 0.9)
7
7
  googleauth (~> 0.4)
@@ -38,7 +38,7 @@ for authorization - for additional information see
38
38
  {here}[https://cloud.google.com/logging/docs/agent/authorization].
39
39
 
40
40
  <em>The previously documented parameters auth_method, private_key_email,
41
- and private_key_path are deprecated and should no longer be used.</em>
41
+ and private_key_path are removed, and can no longer be used.</em>
42
42
 
43
43
  == Copyright
44
44
 
data/Rakefile CHANGED
@@ -16,7 +16,17 @@ Rake::TestTask.new(:test) do |test|
16
16
  test.verbose = true
17
17
  end
18
18
 
19
+ # Building the gem will use the local file mode, so ensure it's world-readable.
20
+ # https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud/issues/53
21
+ desc 'Check plugin file permissions'
22
+ task :check_perms do
23
+ plugin = 'lib/fluent/plugin/out_google_cloud.rb'
24
+ mode = File.stat(plugin).mode & 0777
25
+ fail "Unexpected mode #{mode.to_s(8)} for #{plugin}" unless
26
+ mode & 0444 == 0444
27
+ end
28
+
19
29
  desc 'Run unit tests and RuboCop to check for style violations'
20
- task all: [:test, :rubocop]
30
+ task all: [:test, :rubocop, :check_perms]
21
31
 
22
32
  task default: :all
@@ -10,25 +10,23 @@ eos
10
10
  gem.homepage = \
11
11
  'https://github.com/GoogleCloudPlatform/fluent-plugin-google-cloud'
12
12
  gem.license = 'Apache-2.0'
13
- gem.version = '0.4.17'
13
+ gem.version = '0.5.0'
14
14
  gem.authors = ['Todd Derr', 'Alex Robinson']
15
15
  gem.email = ['salty@google.com']
16
+ gem.required_ruby_version = Gem::Requirement.new('>= 2.0')
16
17
 
17
18
  gem.files = Dir['**/*'].keep_if { |file| File.file?(file) }
18
19
  gem.test_files = gem.files.grep(/^(test)/)
19
20
  gem.require_paths = ['lib']
20
21
 
21
22
  gem.add_runtime_dependency 'fluentd', '~> 0.10'
22
- gem.add_runtime_dependency 'google-api-client', '>= 0.8.6', '< 0.9'
23
+ gem.add_runtime_dependency 'google-api-client', '> 0.9'
23
24
  gem.add_runtime_dependency 'googleauth', '~> 0.4'
24
25
  gem.add_runtime_dependency 'json', '~> 1.8'
25
- # workaround for jwt 1.5.3 breaking ruby 1.9 support (included by googleauth)
26
- # see https://github.com/jwt/ruby-jwt/issues/132
27
- gem.add_runtime_dependency 'jwt', '< 1.5.3'
28
26
 
29
27
  gem.add_development_dependency 'mocha', '~> 1.1'
30
28
  gem.add_development_dependency 'rake', '~> 10.3'
31
- gem.add_development_dependency 'rubocop', '= 0.34.2'
29
+ gem.add_development_dependency 'rubocop', '= 0.35.0'
32
30
  gem.add_development_dependency 'webmock', '~> 1.17'
33
31
  gem.add_development_dependency 'test-unit', '~> 3.0'
34
32
  end
@@ -11,14 +11,13 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- require 'cgi'
15
- require 'google/api_client'
16
- require 'google/api_client/auth/compute_service_account'
17
- require 'googleauth'
18
14
  require 'json'
19
15
  require 'open-uri'
20
16
  require 'socket'
21
17
  require 'yaml'
18
+ require 'google/apis'
19
+ require 'google/apis/logging_v1beta3'
20
+ require 'googleauth'
22
21
 
23
22
  module Fluent
24
23
  # fluentd output plugin for the Google Cloud Logging API
@@ -26,7 +25,7 @@ module Fluent
26
25
  Fluent::Plugin.register_output('google_cloud', self)
27
26
 
28
27
  PLUGIN_NAME = 'Fluentd Google Cloud Logging plugin'
29
- PLUGIN_VERSION = '0.4.17'
28
+ PLUGIN_VERSION = '0.5.0'
30
29
 
31
30
  # Constants for service names.
32
31
  APPENGINE_SERVICE = 'appengine.googleapis.com'
@@ -42,28 +41,9 @@ module Fluent
42
41
  # Address of the metadata service.
43
42
  METADATA_SERVICE_ADDR = '169.254.169.254'
44
43
 
45
- # Fields allowed in httpRequest object
46
- HTTP_REQUEST_FIELDS = %w(requestMethod requestUrl requestSize status
47
- responseSize userAgent remoteIp referer
48
- cacheHit validatedWithOriginServer)
49
-
50
44
  # Disable this warning to conform to fluentd config_param conventions.
51
45
  # rubocop:disable Style/HashSyntax
52
46
 
53
- # DEPRECATED: auth_method (and support for 'private_key') is deprecated in
54
- # favor of Google Application Default Credentials as documented at:
55
- # https://developers.google.com/identity/protocols/application-default-credentials
56
- # 'private_key' is still accepted to support existing users; any other
57
- # value is ignored.
58
- config_param :auth_method, :string, :default => nil
59
-
60
- # DEPRECATED: Parameters necessary to use the private_key auth_method.
61
- config_param :private_key_email, :string, :default => nil
62
- config_param :private_key_path, :string, :default => nil
63
- config_param :private_key_passphrase, :string,
64
- :default => 'notasecret',
65
- :secret => true
66
-
67
47
  # Specify project/instance metadata.
68
48
  #
69
49
  # project_id, zone, and vm_id are required to have valid values, which
@@ -117,6 +97,15 @@ module Fluent
117
97
  # }
118
98
  config_param :label_map, :hash, :default => nil
119
99
 
100
+ # DEPRECATED: The following parameters, if present in the config
101
+ # indicate that the plugin configuration must be updated.
102
+ config_param :auth_method, :string, :default => nil
103
+ config_param :private_key_email, :string, :default => nil
104
+ config_param :private_key_path, :string, :default => nil
105
+ config_param :private_key_passphrase, :string,
106
+ :default => nil,
107
+ :secret => true
108
+
120
109
  # rubocop:enable Style/HashSyntax
121
110
 
122
111
  # TODO: Add a log_name config option rather than just using the tag?
@@ -141,21 +130,19 @@ module Fluent
141
130
  def configure(conf)
142
131
  super
143
132
 
144
- unless @auth_method.nil?
145
- @log.warn 'auth_method is deprecated; please migrate to using ' \
146
- 'Application Default Credentials.'
147
- if @auth_method == 'private_key'
148
- if !@private_key_email
149
- fail Fluent::ConfigError, '"private_key_email" must be ' \
150
- 'specified if auth_method is "private_key"'
151
- elsif !@private_key_path
152
- fail Fluent::ConfigError, '"private_key_path" must be ' \
153
- 'specified if auth_method is "private_key"'
154
- elsif !@private_key_passphrase
155
- fail Fluent::ConfigError, '"private_key_passphrase" must be ' \
156
- 'specified if auth_method is "private_key"'
157
- end
158
- end
133
+ # Alert on old authentication configuration.
134
+ unless @auth_method.nil? && @private_key_email.nil? &&
135
+ @private_key_path.nil? && @private_key_passphrase.nil?
136
+ extra = []
137
+ extra << 'auth_method' unless @auth_method.nil?
138
+ extra << 'private_key_email' unless @private_key_email.nil?
139
+ extra << 'private_key_path' unless @private_key_path.nil?
140
+ extra << 'private_key_passphrase' unless @private_key_passphrase.nil?
141
+
142
+ fail Fluent::ConfigError,
143
+ "#{PLUGIN_NAME} no longer supports auth_method.\n" \
144
+ 'Please remove configuration parameters: ' +
145
+ extra.join(' ')
159
146
  end
160
147
 
161
148
  # TODO: Send instance tags as labels as well?
@@ -350,14 +337,13 @@ module Fluent
350
337
  arr.each do |time, record|
351
338
  next unless record.is_a?(Hash)
352
339
 
353
- entry = {
354
- 'metadata' => {
355
- 'serviceName' => @service_name,
356
- 'projectId' => @project_id,
357
- 'zone' => @zone,
358
- 'labels' => {}
359
- }
360
- }
340
+ entry = Google::Apis::LoggingV1beta3::LogEntry.new(
341
+ metadata: Google::Apis::LoggingV1beta3::LogEntryMetadata.new(
342
+ service_name: @service_name,
343
+ project_id: @project_id,
344
+ zone: @zone,
345
+ labels: {}
346
+ ))
361
347
 
362
348
  if @service_name == CLOUDFUNCTIONS_SERVICE && record.key?('log')
363
349
  @cloudfunctions_log_match =
@@ -365,7 +351,7 @@ module Fluent
365
351
  end
366
352
  if @service_name == CONTAINER_SERVICE
367
353
  # Move the stdout/stderr annotation from the record into a label
368
- field_to_label(record, 'stream', entry['metadata']['labels'],
354
+ field_to_label(record, 'stream', entry.metadata.labels,
369
355
  "#{CONTAINER_SERVICE}/stream")
370
356
  # If the record has been annotated by the kubernetes_metadata_filter
371
357
  # plugin, then use that metadata. Otherwise, rely on commonLabels
@@ -403,87 +389,75 @@ module Fluent
403
389
  # and do not send that field as part of the payload.
404
390
  if @label_map
405
391
  @label_map.each do |field, label|
406
- field_to_label(record, field, entry['metadata']['labels'], label)
392
+ field_to_label(record, field, entry.metadata.labels, label)
407
393
  end
408
394
  end
409
395
 
410
396
  if @service_name == CLOUDFUNCTIONS_SERVICE &&
411
397
  @cloudfunctions_log_match &&
412
398
  @cloudfunctions_log_match['execution_id']
413
- entry['metadata']['labels']['execution_id'] =
399
+ entry.metadata.labels['execution_id'] =
414
400
  @cloudfunctions_log_match['execution_id']
415
401
  end
416
402
 
417
403
  set_payload(record, entry, is_container_json)
418
-
419
- # Remove the labels metadata if we didn't populate it with anything.
420
- if entry['metadata']['labels'].empty?
421
- entry['metadata'].delete('labels')
422
- end
404
+ entry.metadata.labels = nil if entry.metadata.labels.empty?
423
405
 
424
406
  entries.push(entry)
425
407
  end
426
408
  # Don't send an empty request if we rejected all the entries.
427
409
  next if entries.empty?
428
410
 
429
- log_name = CGI.escape(log_name(tag, labels))
430
- url = 'https://logging.googleapis.com/v1beta3/projects/' \
431
- "#{@project_id}/logs/#{log_name}/entries:write"
411
+ log_name = log_name(tag, labels)
432
412
 
433
413
  begin
414
+ # Does the actual write to the cloud logging api.
415
+ # The URI of the write is constructed by the Google::Api request;
416
+ # it is equivalent to this URL:
417
+ # 'https://logging.googleapis.com/v1beta3/projects/' \
418
+ # "#{@project_id}/logs/#{log_name}/entries:write"
419
+
434
420
  client = api_client
435
- request = client.generate_request(
436
- uri: url,
437
- http_method: 'POST',
438
- authenticated: true,
439
- body_object: {
440
- 'commonLabels' => labels,
441
- 'entries' => entries
442
- }
443
- )
444
- client.execute!(request)
421
+
422
+ write_request = \
423
+ Google::Apis::LoggingV1beta3::WriteLogEntriesRequest.new(
424
+ common_labels: labels,
425
+ entries: entries)
426
+
427
+ # TODO: RequestOptions
428
+ client.write_log_entries(@project_id, log_name, write_request)
429
+
445
430
  # Let the user explicitly know when the first call succeeded,
446
431
  # to aid with verification and troubleshooting.
447
432
  unless @successful_call
448
433
  @successful_call = true
449
434
  @log.info 'Successfully sent to Google Cloud Logging API.'
450
435
  end
451
- # Allow most exceptions to propagate, which will cause fluentd to
452
- # retry (with backoff), but in some cases we catch the error and
453
- # drop the request (we will emit a log message in those cases).
454
- rescue Google::APIClient::ClientError => error
436
+
437
+ rescue Google::Apis::ServerError => error
438
+ # Server error, so retry via re-raising the error.
439
+ raise error
440
+
441
+ rescue Google::Apis::AuthorizationError => error
442
+ # Authorization error.
443
+ # These are usually solved via a `gcloud auth` call, or by modifying
444
+ # the permissions on the Google Cloud project.
445
+ dropped = entries.length
446
+ @log.warn "Dropping #{dropped} log message(s)",
447
+ error_class: error.class.to_s, error: error.to_s
448
+
449
+ rescue Google::Apis::ClientError => error
455
450
  # Most ClientErrors indicate a problem with the request itself and
456
- # should not be retried, unless it is an authentication issue, in
457
- # which case we will retry the request via re-raising the exception.
458
- raise error if retriable_client_error?(error)
459
- log_write_failure(entries.length, error)
460
- rescue JSON::GeneratorError => error
461
- # This happens if the request contains illegal characters;
462
- # do not retry it because it will fail repeatedly.
463
- log_write_failure(entries.length, error)
451
+ # should not be retried.
452
+ dropped = entries.length
453
+ @log.warn "Dropping #{dropped} log message(s)",
454
+ error_class: error.class.to_s, error: error.to_s
464
455
  end
465
456
  end
466
457
  end
467
458
 
468
459
  private
469
460
 
470
- RETRIABLE_CLIENT_ERRORS = Set.new [
471
- 'Invalid Credentials',
472
- 'Request had invalid credentials.',
473
- 'The caller does not have permission',
474
- 'Project has not enabled the API. Please use Google Developers ' \
475
- 'Console to activate the API for your project.',
476
- 'Unable to fetch access token (no scopes configured?)']
477
-
478
- def retriable_client_error?(error)
479
- RETRIABLE_CLIENT_ERRORS.include?(error.message)
480
- end
481
-
482
- def log_write_failure(dropped, error)
483
- @log.warn "Dropping #{dropped} log message(s)",
484
- error_class: error.class.to_s, error: error.to_s
485
- end
486
-
487
461
  def parse_json_or_nil(input)
488
462
  # Only here to please rubocop...
489
463
  return nil if input.nil?
@@ -512,11 +486,11 @@ module Fluent
512
486
 
513
487
  begin
514
488
  open('http://' + METADATA_SERVICE_ADDR) do |f|
515
- if (f.meta['metadata-flavor'] == 'Google')
489
+ if f.meta['metadata-flavor'] == 'Google'
516
490
  @log.info 'Detected GCE platform'
517
491
  return Platform::GCE
518
492
  end
519
- if (f.meta['server'] == 'EC2ws')
493
+ if f.meta['server'] == 'EC2ws'
520
494
  @log.info 'Detected EC2 platform'
521
495
  return Platform::EC2
522
496
  end
@@ -554,7 +528,6 @@ module Fluent
554
528
  # Determine the project ID from the credentials, if possible.
555
529
  # Returns the project ID (as a string) on success, or nil on failure.
556
530
  def self.project_id
557
- return nil if @auth_method == 'private_key'
558
531
  creds = Google::Auth.get_application_default(LOGGING_SCOPE)
559
532
  if creds.issuer
560
533
  id = extract_project_id(creds.issuer)
@@ -610,16 +583,14 @@ module Fluent
610
583
  record.delete('timestamp')
611
584
  elsif record.key?('timestampSeconds') &&
612
585
  record.key?('timestampNanos')
613
- ts_secs = record['timestampSeconds']
614
- ts_nanos = record['timestampNanos']
615
- record.delete('timestampSeconds')
616
- record.delete('timestampNanos')
586
+ ts_secs = record.delete('timestampSeconds')
587
+ ts_nanos = record.delete('timestampNanos')
617
588
  elsif record.key?('timeNanos')
618
589
  # This is deprecated since the precision is insufficient.
619
590
  # Use timestampSeconds/timestampNanos instead
620
- ts_secs = (record['timeNanos'] / 1_000_000_000).to_i
621
- ts_nanos = record['timeNanos'] % 1_000_000_000
622
- record.delete('timeNanos')
591
+ nanos = record.delete('timeNanos')
592
+ ts_secs = (nanos / 1_000_000_000).to_i
593
+ ts_nanos = nanos % 1_000_000_000
623
594
  unless @timenanos_warning
624
595
  # Warn the user this is deprecated, but only once to avoid spam.
625
596
  @timenanos_warning = true
@@ -636,47 +607,51 @@ module Fluent
636
607
  ts_secs = timestamp.tv_sec
637
608
  ts_nanos = timestamp.tv_nsec
638
609
  end
639
- entry['metadata']['timestamp'] = {
640
- 'seconds' => ts_secs,
641
- 'nanos' => ts_nanos
610
+ entry.metadata.timestamp = {
611
+ seconds: ts_secs,
612
+ nanos: ts_nanos
642
613
  }
643
614
  end
644
615
 
645
616
  def set_severity(record, entry)
646
617
  if @service_name == CLOUDFUNCTIONS_SERVICE
647
618
  if @cloudfunctions_log_match && @cloudfunctions_log_match['severity']
648
- entry['metadata']['severity'] =
619
+ entry.metadata.severity =
649
620
  parse_severity(@cloudfunctions_log_match['severity'])
650
621
  elsif record.key?('stream') && record['stream'] == 'stdout'
651
- entry['metadata']['severity'] = 'INFO'
622
+ entry.metadata.severity = 'INFO'
652
623
  record.delete('stream')
653
624
  elsif record.key?('stream') && record['stream'] == 'stderr'
654
- entry['metadata']['severity'] = 'ERROR'
625
+ entry.metadata.severity = 'ERROR'
655
626
  record.delete('stream')
656
627
  else
657
- entry['metadata']['severity'] = 'DEFAULT'
628
+ entry.metadata.severity = 'DEFAULT'
658
629
  end
659
630
  elsif record.key?('severity')
660
- entry['metadata']['severity'] = parse_severity(record['severity'])
631
+ entry.metadata.severity = parse_severity(record['severity'])
661
632
  record.delete('severity')
662
633
  else
663
- entry['metadata']['severity'] = 'DEFAULT'
634
+ entry.metadata.severity = 'DEFAULT'
664
635
  end
665
636
  end
666
637
 
667
638
  def set_http_request(record, entry)
668
- return unless record['httpRequest'].is_a?(Hash)
669
-
670
- entry['httpRequest'] = {}
671
-
672
- HTTP_REQUEST_FIELDS.each do |field|
673
- if record['httpRequest'].key?(field)
674
- entry['httpRequest'][field] = record['httpRequest'][field]
675
- record['httpRequest'].delete(field)
676
- end
677
- end
678
-
679
- record.delete('httpRequest') if record['httpRequest'].empty?
639
+ return nil unless record['httpRequest'].is_a?(Hash)
640
+ input = record['httpRequest']
641
+ output = Google::Apis::LoggingV1beta3::HttpRequest.new
642
+ output.request_method = input.delete('requestMethod')
643
+ output.request_url = input.delete('requestUrl')
644
+ output.request_size = input.delete('requestSize')
645
+ output.status = input.delete('status')
646
+ output.response_size = input.delete('responseSize')
647
+ output.user_agent = input.delete('userAgent')
648
+ output.remote_ip = input.delete('remoteIp')
649
+ output.referer = input.delete('referer')
650
+ output.cache_hit = input.delete('cacheHit')
651
+ output.validated_with_origin_server = \
652
+ input.delete('validatedWithOriginServer')
653
+ record.delete('httpRequest') if input.empty?
654
+ entry.http_request = output
680
655
  end
681
656
 
682
657
  # Values permitted by the API for 'severity' (which is an enum).
@@ -748,13 +723,13 @@ module Fluent
748
723
  def handle_container_metadata(record, entry)
749
724
  fields = %w(namespace_id namespace_name pod_id pod_name container_name)
750
725
  fields.each do |field|
751
- field_to_label(record['kubernetes'], field, entry['metadata']['labels'],
726
+ field_to_label(record['kubernetes'], field, entry.metadata.labels,
752
727
  "#{CONTAINER_SERVICE}/#{field}")
753
728
  end
754
729
  # Prepend label/ to all user-defined labels' keys.
755
730
  if record['kubernetes'].key?('labels')
756
731
  record['kubernetes']['labels'].each do |key, value|
757
- entry['metadata']['labels']["label/#{key}"] = value
732
+ entry.metadata.labels["label/#{key}"] = value
758
733
  end
759
734
  end
760
735
  # We've explicitly consumed all the fields we care about -- don't litter
@@ -777,20 +752,20 @@ module Fluent
777
752
  # 3. This is an unstructured Container log and the 'log' key is available
778
753
  # 4. The only remaining key is 'message'
779
754
  if @service_name == CLOUDFUNCTIONS_SERVICE && @cloudfunctions_log_match
780
- entry['textPayload'] = @cloudfunctions_log_match['text']
755
+ entry.text_payload = @cloudfunctions_log_match['text']
781
756
  elsif @service_name == CLOUDFUNCTIONS_SERVICE && record.key?('log')
782
- entry['textPayload'] = record['log']
757
+ entry.text_payload = record['log']
783
758
  elsif @service_name == CONTAINER_SERVICE && record.key?('log') &&
784
759
  !is_container_json
785
- entry['textPayload'] = record['log']
760
+ entry.text_payload = record['log']
786
761
  elsif record.size == 1 && record.key?('message')
787
- entry['textPayload'] = record['message']
762
+ entry.text_payload = record['message']
788
763
  else
789
- entry['structPayload'] = record
764
+ entry.struct_payload = record
790
765
  end
791
766
  end
792
767
 
793
- def log_name(tag, commonLabels)
768
+ def log_name(tag, common_labels)
794
769
  if @service_name == CLOUDFUNCTIONS_SERVICE
795
770
  return 'cloud-functions'
796
771
  elsif @running_on_managed_vm
@@ -800,30 +775,20 @@ module Fluent
800
775
  # For Kubernetes logs, use just the container name as the log name
801
776
  # if we have it.
802
777
  container_name_key = "#{CONTAINER_SERVICE}/container_name"
803
- if commonLabels && commonLabels.key?(container_name_key)
804
- return commonLabels[container_name_key]
778
+ if common_labels && common_labels.key?(container_name_key)
779
+ return common_labels[container_name_key]
805
780
  end
806
781
  end
807
782
  tag
808
783
  end
809
784
 
810
785
  def init_api_client
811
- @client = Google::APIClient.new(
812
- application_name: PLUGIN_NAME,
813
- application_version: PLUGIN_VERSION,
814
- retries: 1)
815
-
816
- if @auth_method == 'private_key'
817
- key = Google::APIClient::PKCS12.load_key(@private_key_path,
818
- @private_key_passphrase)
819
- jwt_asserter = Google::APIClient::JWTAsserter.new(
820
- @private_key_email, LOGGING_SCOPE, key)
821
- @client.authorization = jwt_asserter.to_authorization
822
- @client.authorization.expiry = 3600 # 3600s is the max allowed value
823
- else
824
- @client.authorization = Google::Auth.get_application_default(
825
- LOGGING_SCOPE)
826
- end
786
+ # TODO: Use a non-default ClientOptions object.
787
+ Google::Apis::ClientOptions.default.application_name = PLUGIN_NAME
788
+ Google::Apis::ClientOptions.default.application_version = PLUGIN_VERSION
789
+ @client = Google::Apis::LoggingV1beta3::LoggingService.new
790
+ @client.authorization = Google::Auth.get_application_default(
791
+ LOGGING_SCOPE)
827
792
  end
828
793
 
829
794
  def api_client
@@ -16,6 +16,7 @@ require 'helper'
16
16
  require 'json'
17
17
  require 'mocha/test_unit'
18
18
  require 'webmock/test_unit'
19
+ require 'google/apis'
19
20
 
20
21
  # Unit tests for Google Cloud Logging plugin
21
22
  class GoogleCloudOutputTest < Test::Unit::TestCase
@@ -117,9 +118,9 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
117
118
 
118
119
  # rubocop:disable Metrics/LineLength
119
120
  PRIVATE_KEY_CONFIG = %(
120
- auth_method private_key
121
- private_key_email 271661262351-ft99kc9kjro9rrihq3k2n3s2inbplu0q@developer.gserviceaccount.com
122
- private_key_path test/plugin/data/c31e573fd7f62ed495c9ca3821a5a85cb036dee1-privatekey.p12
121
+ auth_method private_key
122
+ private_key_email 271661262351-ft99kc9kjro9rrihq3k2n3s2inbplu0q@developer.gserviceaccount.com
123
+ private_key_path test/plugin/data/c31e573fd7f62ed495c9ca3821a5a85cb036dee1-privatekey.p12
123
124
  )
124
125
  # rubocop:enable Metrics/LineLength
125
126
 
@@ -138,14 +139,6 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
138
139
  vm_name #{CUSTOM_HOSTNAME}
139
140
  )
140
141
 
141
- CONFIG_MISSING_PRIVATE_KEY_PATH = %(
142
- auth_method private_key
143
- private_key_email nobody@example.com
144
- )
145
- CONFIG_MISSING_PRIVATE_KEY_EMAIL = %(
146
- auth_method private_key
147
- private_key_path /fake/path/to/key
148
- )
149
142
  CONFIG_MISSING_METADATA_PROJECT_ID = %(
150
143
  zone #{CUSTOM_ZONE}
151
144
  vm_id #{CUSTOM_VM_ID}
@@ -331,43 +324,28 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
331
324
  def test_configure_service_account_application_default
332
325
  setup_gce_metadata_stubs
333
326
  d = create_driver(APPLICATION_DEFAULT_CONFIG)
334
- assert d.instance.auth_method.nil?
327
+ assert_equal HOSTNAME, d.instance.vm_name
335
328
  end
336
329
 
337
330
  def test_configure_service_account_private_key
331
+ # Using out-of-date config method.
338
332
  setup_gce_metadata_stubs
339
- d = create_driver(PRIVATE_KEY_CONFIG)
340
- assert_equal 'private_key', d.instance.auth_method
341
- end
342
-
343
- def test_configure_custom_metadata
344
- setup_no_metadata_service_stubs
345
- d = create_driver(CUSTOM_METADATA_CONFIG)
346
- assert_equal CUSTOM_PROJECT_ID, d.instance.project_id
347
- assert_equal CUSTOM_ZONE, d.instance.zone
348
- assert_equal CUSTOM_VM_ID, d.instance.vm_id
349
- end
350
-
351
- def test_configure_invalid_private_key_missing_path
352
333
  exception_count = 0
353
334
  begin
354
- _d = create_driver(CONFIG_MISSING_PRIVATE_KEY_PATH)
335
+ _d = create_driver(PRIVATE_KEY_CONFIG)
355
336
  rescue Fluent::ConfigError => error
356
- assert error.message.include? 'private_key_path'
337
+ assert error.message.include? 'Please remove configuration parameters'
357
338
  exception_count += 1
358
339
  end
359
340
  assert_equal 1, exception_count
360
341
  end
361
342
 
362
- def test_configure_invalid_private_key_missing_email
363
- exception_count = 0
364
- begin
365
- _d = create_driver(CONFIG_MISSING_PRIVATE_KEY_EMAIL)
366
- rescue Fluent::ConfigError => error
367
- assert error.message.include? 'private_key_email'
368
- exception_count += 1
369
- end
370
- assert_equal 1, exception_count
343
+ def test_configure_custom_metadata
344
+ setup_no_metadata_service_stubs
345
+ d = create_driver(CUSTOM_METADATA_CONFIG)
346
+ assert_equal CUSTOM_PROJECT_ID, d.instance.project_id
347
+ assert_equal CUSTOM_ZONE, d.instance.zone
348
+ assert_equal CUSTOM_VM_ID, d.instance.vm_id
371
349
  end
372
350
 
373
351
  def test_configure_invalid_metadata_missing_project_id_no_metadata_service
@@ -569,15 +547,6 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
569
547
  assert_equal 1, exception_count
570
548
  end
571
549
 
572
- def test_one_log_private_key
573
- setup_gce_metadata_stubs
574
- setup_logging_stubs
575
- d = create_driver(PRIVATE_KEY_CONFIG)
576
- d.emit('message' => log_entry(0))
577
- d.run
578
- verify_log_entries(1, COMPUTE_PARAMS)
579
- end
580
-
581
550
  def test_one_log_custom_metadata
582
551
  # don't set up any metadata stubs, so the test will fail if we try to
583
552
  # fetch metadata (and explicitly check this as well).
@@ -795,7 +764,7 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
795
764
  assert @logs_sent.empty?
796
765
  end
797
766
 
798
- def test_client_error
767
+ def test_client_400
799
768
  setup_gce_metadata_stubs
800
769
  # The API Client should not retry this and the plugin should consume
801
770
  # the exception.
@@ -807,43 +776,19 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
807
776
  assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 1)
808
777
  end
809
778
 
810
- # helper for the ClientError retriable special cases below.
811
- def client_error_helper(message)
779
+ # All credentials errors resolve to a 401.
780
+ def test_client_401
812
781
  setup_gce_metadata_stubs
813
782
  stub_request(:post, uri_for_log(COMPUTE_PARAMS))
814
- .to_return(status: 401, body: message)
783
+ .to_return(status: 401, body: 'Unauthorized')
815
784
  d = create_driver
816
785
  d.emit('message' => log_entry(0))
817
- exception_count = 0
818
786
  begin
819
787
  d.run
820
- rescue Google::APIClient::ClientError => error
821
- assert_equal message, error.message
822
- exception_count += 1
788
+ rescue Google::Apis::AuthorizationError => error
789
+ assert_equal 'Unauthorized', error.message
823
790
  end
824
791
  assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 2)
825
- assert_equal 1, exception_count
826
- end
827
-
828
- def test_client_error_invalid_credentials
829
- client_error_helper('Invalid Credentials')
830
- end
831
-
832
- def test_client_error_caller_does_not_have_permission
833
- client_error_helper('The caller does not have permission')
834
- end
835
-
836
- def test_client_error_request_had_invalid_credentials
837
- client_error_helper('Request had invalid credentials.')
838
- end
839
-
840
- def test_client_error_project_has_not_enabled_the_api
841
- client_error_helper('Project has not enabled the API. Please use ' \
842
- 'Google Developers Console to activate the API for your project.')
843
- end
844
-
845
- def test_client_error_unable_to_fetch_accesss_token
846
- client_error_helper('Unable to fetch access token (no scopes configured?)')
847
792
  end
848
793
 
849
794
  def test_server_error
@@ -857,11 +802,11 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
857
802
  exception_count = 0
858
803
  begin
859
804
  d.run
860
- rescue Google::APIClient::ServerError => error
861
- assert_equal 'Server Error', error.message
805
+ rescue Google::Apis::ServerError => error
806
+ assert_equal 'Server error', error.message
862
807
  exception_count += 1
863
808
  end
864
- assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 2)
809
+ assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 1)
865
810
  assert_equal 1, exception_count
866
811
  end
867
812
 
@@ -1266,14 +1211,6 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
1266
1211
  status: 200,
1267
1212
  headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
1268
1213
  'Content-Type' => 'application/json' })
1269
-
1270
- # Used for 'private_key' auth.
1271
- stub_request(:post, 'https://accounts.google.com/o/oauth2/token')
1272
- .with(body: hash_including(grant_type: AUTH_GRANT_TYPE))
1273
- .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
1274
- status: 200,
1275
- headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
1276
- 'Content-Type' => 'application/json' })
1277
1214
  end
1278
1215
 
1279
1216
  def setup_managed_vm_metadata_stubs
@@ -1376,7 +1313,7 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
1376
1313
  assert entry.key?(payload_type), 'Entry did not contain expected ' \
1377
1314
  "#{payload_type} key: " + entry.to_s
1378
1315
  # Check the payload for textPayload, otherwise it's up to the caller.
1379
- if (payload_type == 'textPayload')
1316
+ if payload_type == 'textPayload'
1380
1317
  assert_equal "test log entry #{i}", entry['textPayload'], batch
1381
1318
  end
1382
1319
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-google-cloud
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.17
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd Derr
@@ -29,20 +29,14 @@ dependencies:
29
29
  name: google-api-client
30
30
  requirement: !ruby/object:Gem::Requirement
31
31
  requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: 0.8.6
35
- - - "<"
32
+ - - ">"
36
33
  - !ruby/object:Gem::Version
37
34
  version: '0.9'
38
35
  type: :runtime
39
36
  prerelease: false
40
37
  version_requirements: !ruby/object:Gem::Requirement
41
38
  requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- version: 0.8.6
45
- - - "<"
39
+ - - ">"
46
40
  - !ruby/object:Gem::Version
47
41
  version: '0.9'
48
42
  - !ruby/object:Gem::Dependency
@@ -73,20 +67,6 @@ dependencies:
73
67
  - - "~>"
74
68
  - !ruby/object:Gem::Version
75
69
  version: '1.8'
76
- - !ruby/object:Gem::Dependency
77
- name: jwt
78
- requirement: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "<"
81
- - !ruby/object:Gem::Version
82
- version: 1.5.3
83
- type: :runtime
84
- prerelease: false
85
- version_requirements: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - "<"
88
- - !ruby/object:Gem::Version
89
- version: 1.5.3
90
70
  - !ruby/object:Gem::Dependency
91
71
  name: mocha
92
72
  requirement: !ruby/object:Gem::Requirement
@@ -121,14 +101,14 @@ dependencies:
121
101
  requirements:
122
102
  - - '='
123
103
  - !ruby/object:Gem::Version
124
- version: 0.34.2
104
+ version: 0.35.0
125
105
  type: :development
126
106
  prerelease: false
127
107
  version_requirements: !ruby/object:Gem::Requirement
128
108
  requirements:
129
109
  - - '='
130
110
  - !ruby/object:Gem::Version
131
- version: 0.34.2
111
+ version: 0.35.0
132
112
  - !ruby/object:Gem::Dependency
133
113
  name: webmock
134
114
  requirement: !ruby/object:Gem::Requirement
@@ -194,7 +174,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
194
174
  requirements:
195
175
  - - ">="
196
176
  - !ruby/object:Gem::Version
197
- version: '0'
177
+ version: '2.0'
198
178
  required_rubygems_version: !ruby/object:Gem::Requirement
199
179
  requirements:
200
180
  - - ">="