fluent-plugin-google-cloud 0.6.8 → 0.6.9.pre.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5e8ef1f49a71df9fe1f3d06422ab70861af8b236
4
- data.tar.gz: 75944aae3e5ea94e1d90ddf17e4ebda82eb2a75a
3
+ metadata.gz: 88272d0a151ead26a57ac7b523602e566a2c6e79
4
+ data.tar.gz: 3161e4dd7387a67af1c725a5423165f42d85a448
5
5
  SHA512:
6
- metadata.gz: 859c5b09cf8130aebdba203a2683d13e7f605eaebff7978b829d7f128e16999e4bc1272282f85ab79b8209eef175bae62e523941dcf3947b8cc9da030bd51f84
7
- data.tar.gz: 38987eb08a7d895d830a0d586a5fcd7bbc2594ef3863977e9518db7bad085e241857e1852f485027dddcce039c48e880cc58ae565734efa5bc1b093c798a7df2
6
+ metadata.gz: b3e2e2acd6e6b7083bf806ad2cdda05f2bb0014cb751a2ee7777b05c21428e2c6256ff16764652b337700f8568df3e02b3e055e3ce8da8505f24e95220e50f6f
7
+ data.tar.gz: d9bed2291c45799ead9d6ef38d3f59bfb9e49f26c34b141e1f2f7db60e4bdc7623304d276b468d6816b02981b8439a39a5158d23b954bc319a93922db1b0e786
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fluent-plugin-google-cloud (0.6.8)
4
+ fluent-plugin-google-cloud (0.6.9.pre.1)
5
5
  fluentd (~> 0.10)
6
- google-api-client (~> 0.9.0)
7
- google-cloud-logging (= 0.24.1)
6
+ google-api-client (~> 0.14)
7
+ google-cloud-logging (~> 1.2, >= 1.2.3)
8
8
  googleapis-common-protos (~> 1.3)
9
9
  googleauth (~> 0.4, < 0.5.2)
10
10
  grpc (~> 1.0, < 1.3)
@@ -21,6 +21,8 @@ GEM
21
21
  cool.io (1.5.1)
22
22
  crack (0.4.3)
23
23
  safe_yaml (~> 1.0.0)
24
+ declarative (0.0.10)
25
+ declarative-option (0.1.0)
24
26
  faraday (0.13.1)
25
27
  multipart-post (>= 1.2, < 3)
26
28
  fluentd (0.14.21)
@@ -34,22 +36,23 @@ GEM
34
36
  tzinfo (~> 1.0)
35
37
  tzinfo-data (~> 1.0)
36
38
  yajl-ruby (~> 1.0)
37
- google-api-client (0.9.28)
38
- addressable (~> 2.3)
39
+ google-api-client (0.15.0)
40
+ addressable (~> 2.5, >= 2.5.1)
39
41
  googleauth (~> 0.5)
40
- httpclient (~> 2.7)
41
- hurley (~> 0.1)
42
- memoist (~> 0.11)
43
- mime-types (>= 1.6)
44
- representable (~> 2.3.0)
45
- retriable (~> 2.0)
46
- google-cloud-core (0.21.1)
42
+ httpclient (>= 2.8.1, < 3.0)
43
+ mime-types (~> 3.0)
44
+ representable (~> 3.0)
45
+ retriable (>= 2.0, < 4.0)
46
+ google-cloud-core (1.0.0)
47
+ google-cloud-env (~> 1.0)
47
48
  googleauth (~> 0.5.1)
48
- google-cloud-logging (0.24.1)
49
- google-cloud-core (~> 0.21.1)
49
+ google-cloud-env (1.0.1)
50
+ faraday (~> 0.11)
51
+ google-cloud-logging (1.2.3)
52
+ google-cloud-core (~> 1.0)
50
53
  google-gax (~> 0.8.0)
51
- stackdriver-core (~> 0.21.0)
52
- google-gax (0.8.7)
54
+ stackdriver-core (~> 1.2)
55
+ google-gax (0.8.10)
53
56
  google-protobuf (~> 3.2)
54
57
  googleapis-common-protos (~> 1.3.5)
55
58
  googleauth (~> 0.5.1)
@@ -73,10 +76,9 @@ GEM
73
76
  grpc (1.2.5)
74
77
  google-protobuf (~> 3.1)
75
78
  googleauth (~> 0.5.1)
76
- hashdiff (0.3.6)
79
+ hashdiff (0.3.7)
77
80
  http_parser.rb (0.6.0)
78
81
  httpclient (2.8.3)
79
- hurley (0.2)
80
82
  json (1.8.6)
81
83
  jwt (1.5.6)
82
84
  little-plugger (1.1.4)
@@ -105,9 +107,11 @@ GEM
105
107
  rainbow (2.2.2)
106
108
  rake
107
109
  rake (10.5.0)
108
- representable (2.3.0)
109
- uber (~> 0.0.7)
110
- retriable (2.1.0)
110
+ representable (3.0.4)
111
+ declarative (< 0.1.0)
112
+ declarative-option (< 0.2.0)
113
+ uber (< 0.2.0)
114
+ retriable (3.1.1)
111
115
  rly (0.2.3)
112
116
  rubocop (0.35.1)
113
117
  astrolabe (~> 1.3)
@@ -116,18 +120,18 @@ GEM
116
120
  rainbow (>= 1.99.1, < 3.0)
117
121
  ruby-progressbar (~> 1.7)
118
122
  tins (<= 1.6.0)
119
- ruby-progressbar (1.8.3)
123
+ ruby-progressbar (1.9.0)
120
124
  ruby_dig (0.0.2)
121
125
  safe_yaml (1.0.4)
122
126
  serverengine (2.0.5)
123
127
  sigdump (~> 0.2.2)
124
128
  sigdump (0.2.4)
125
- signet (0.7.3)
129
+ signet (0.8.1)
126
130
  addressable (~> 2.3)
127
131
  faraday (~> 0.9)
128
- jwt (~> 1.5)
132
+ jwt (>= 1.5, < 3.0)
129
133
  multi_json (~> 1.10)
130
- stackdriver-core (0.21.0)
134
+ stackdriver-core (1.2.0)
131
135
  strptime (0.1.9)
132
136
  test-unit (3.2.6)
133
137
  power_assert
@@ -137,7 +141,7 @@ GEM
137
141
  thread_safe (~> 0.1)
138
142
  tzinfo-data (1.2017.2)
139
143
  tzinfo (>= 1.0.0)
140
- uber (0.0.15)
144
+ uber (0.1.0)
141
145
  webmock (2.3.2)
142
146
  addressable (>= 2.3.6)
143
147
  crack (>= 0.3.2)
@@ -10,7 +10,7 @@ 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.6.8'
13
+ gem.version = '0.6.9.pre.1'
14
14
  gem.authors = ['Todd Derr', 'Alex Robinson']
15
15
  gem.email = ['salty@google.com']
16
16
  gem.required_ruby_version = Gem::Requirement.new('>= 2.0')
@@ -21,8 +21,8 @@ eos
21
21
 
22
22
  gem.add_runtime_dependency 'fluentd', '~> 0.10'
23
23
  gem.add_runtime_dependency 'googleapis-common-protos', '~> 1.3'
24
- gem.add_runtime_dependency 'google-api-client', '~> 0.9.0'
25
- gem.add_runtime_dependency 'google-cloud-logging', '0.24.1'
24
+ gem.add_runtime_dependency 'google-api-client', '~> 0.14'
25
+ gem.add_runtime_dependency 'google-cloud-logging', '~> 1.2', '>= 1.2.3'
26
26
  gem.add_runtime_dependency 'googleauth', '~> 0.4', '< 0.5.2'
27
27
  gem.add_runtime_dependency 'grpc', '~> 1.0', '< 1.3'
28
28
  gem.add_runtime_dependency 'json', '~> 1.8'
@@ -11,6 +11,7 @@
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 'erb'
14
15
  require 'grpc'
15
16
  require 'json'
16
17
  require 'open-uri'
@@ -18,7 +19,7 @@ require 'socket'
18
19
  require 'time'
19
20
  require 'yaml'
20
21
  require 'google/apis'
21
- require 'google/apis/logging_v2beta1'
22
+ require 'google/apis/logging_v2'
22
23
  require 'google/logging/v2/logging_pb'
23
24
  require 'google/logging/v2/logging_services_pb'
24
25
  require 'google/logging/v2/log_entry_pb'
@@ -67,7 +68,7 @@ module Fluent
67
68
  DATAFLOW_CONSTANTS = {
68
69
  service: 'dataflow.googleapis.com',
69
70
  resource_type: 'dataflow_step',
70
- extra_common_labels: %w(region job_name job_id step_id)
71
+ extra_resource_labels: %w(region job_name job_id step_id)
71
72
  }
72
73
  DATAPROC_CONSTANTS = {
73
74
  service: 'cluster.dataproc.googleapis.com',
@@ -81,7 +82,7 @@ module Fluent
81
82
  ML_CONSTANTS = {
82
83
  service: 'ml.googleapis.com',
83
84
  resource_type: 'ml_job',
84
- extra_common_labels: %w(job_id task_name)
85
+ extra_resource_labels: %w(job_id task_name)
85
86
  }
86
87
 
87
88
  # The map between a subservice name and a resource type.
@@ -112,7 +113,7 @@ module Fluent
112
113
  'http://local-metadata-agent.stackdriver.com:8000'
113
114
  end
114
115
 
115
- # Constants for log entry field extraction.
116
+ # Internal constants.
116
117
  module InternalConstants
117
118
  # The label name of local_resource_id in the json payload. When a record
118
119
  # has this field in the payload, we will use the value to retrieve
@@ -144,7 +145,7 @@ module Fluent
144
145
  # The grpc version class name.
145
146
  'Google::Logging::Type::HttpRequest',
146
147
  # The non-grpc version class name.
147
- 'Google::Apis::LoggingV2beta1::HttpRequest'
148
+ 'Google::Apis::LoggingV2::HttpRequest'
148
149
  ],
149
150
  'source_location' => [
150
151
  '@source_location_key',
@@ -154,7 +155,7 @@ module Fluent
154
155
  %w(line line parse_int)
155
156
  ],
156
157
  'Google::Logging::V2::LogEntrySourceLocation',
157
- 'Google::Apis::LoggingV2beta1::LogEntrySourceLocation'
158
+ 'Google::Apis::LoggingV2::LogEntrySourceLocation'
158
159
  ],
159
160
  'operation' => [
160
161
  '@operation_key',
@@ -165,9 +166,13 @@ module Fluent
165
166
  %w(last last parse_bool)
166
167
  ],
167
168
  'Google::Logging::V2::LogEntryOperation',
168
- 'Google::Apis::LoggingV2beta1::LogEntryOperation'
169
+ 'Google::Apis::LoggingV2::LogEntryOperation'
169
170
  ]
170
171
  }
172
+
173
+ # The name of the WriteLogEntriesPartialErrors field in the error details.
174
+ PARTIAL_ERROR_FIELD =
175
+ 'type.googleapis.com/google.logging.v2.WriteLogEntriesPartialErrors'
171
176
  end
172
177
 
173
178
  include self::ServiceConstants
@@ -177,7 +182,7 @@ module Fluent
177
182
  Fluent::Plugin.register_output('google_cloud', self)
178
183
 
179
184
  PLUGIN_NAME = 'Fluentd Google Cloud Logging plugin'
180
- PLUGIN_VERSION = '0.6.8'
185
+ PLUGIN_VERSION = '0.6.9.pre.1'
181
186
 
182
187
  # Name of the the Google cloud logging write scope.
183
188
  LOGGING_SCOPE = 'https://www.googleapis.com/auth/logging.write'
@@ -280,6 +285,12 @@ module Fluent
280
285
  # Cloud Logging API.
281
286
  config_param :use_grpc, :bool, :default => false
282
287
 
288
+ # Whether valid entries should be written even if some other entries fail
289
+ # due to INVALID_ARGUMENT or PERMISSION_DENIED errors when communicating to
290
+ # the Cloud Logging API. This is highly recommended. Right now this only
291
+ # works with the REST path (use_grpc = false).
292
+ config_param :partial_success, :bool, :default => false
293
+
283
294
  # Whether to allow non-UTF-8 characters in user logs. If set to true, any
284
295
  # non-UTF-8 character would be replaced by the string specified by
285
296
  # 'non_utf8_replacement_string'. If set to false, any non-UTF-8 character
@@ -338,6 +349,15 @@ module Fluent
338
349
  def configure(conf)
339
350
  super
340
351
 
352
+ # TODO(qingling128): Remove this warning after the support is added. Also
353
+ # remove the comment in the description of this configuration.
354
+ if @partial_success && @use_grpc
355
+ @log.warn 'Detected partial_success enabled while use_grpc is also' \
356
+ ' enabled. The support for partial success in the gRPC path' \
357
+ ' is to be added in the near future. For now the ' \
358
+ ' partial_success flag will be ignored.'
359
+ end
360
+
341
361
  # If monitoring is enabled, register metrics in the default registry
342
362
  # and store metric objects for future use.
343
363
  if @enable_monitoring
@@ -519,7 +539,7 @@ module Fluent
519
539
  # Remove the labels if we didn't populate them with anything.
520
540
  entry_level_resource.labels = nil if
521
541
  entry_level_resource.labels.empty?
522
- entry = Google::Apis::LoggingV2beta1::LogEntry.new(
542
+ entry = Google::Apis::LoggingV2::LogEntry.new(
523
543
  labels: entry_level_common_labels,
524
544
  resource: entry_level_resource,
525
545
  severity: severity,
@@ -622,20 +642,24 @@ module Fluent
622
642
  else
623
643
  begin
624
644
  write_request = \
625
- Google::Apis::LoggingV2beta1::WriteLogEntriesRequest.new(
645
+ Google::Apis::LoggingV2::WriteLogEntriesRequest.new(
626
646
  log_name: log_name,
627
647
  resource: group_level_resource,
628
648
  labels: group_level_common_labels,
649
+ partial_success: @partial_success,
629
650
  entries: entries)
630
651
  entries_count = entries.length
631
652
 
632
- # TODO: RequestOptions
633
653
  begin
634
- client.write_entry_log_entries(write_request)
654
+ client.write_entry_log_entries(
655
+ write_request,
656
+ options: { api_format_version: '2' }
657
+ )
635
658
  rescue Google::Apis::Error => error
636
659
  increment_failed_requests_count(error.status_code)
637
660
  raise error
638
661
  end
662
+
639
663
  increment_successful_requests_count
640
664
  increment_ingested_entries_count(entries_count)
641
665
 
@@ -664,9 +688,20 @@ module Fluent
664
688
  rescue Google::Apis::ClientError => error
665
689
  # Most ClientErrors indicate a problem with the request itself and
666
690
  # should not be retried.
667
- increment_dropped_entries_count(entries_count)
668
- @log.warn "Dropping #{entries_count} log message(s)",
669
- error_class: error.class.to_s, error: error.to_s
691
+ error_details_map = construct_error_details_map(error)
692
+ if error_details_map.empty?
693
+ increment_dropped_entries_count(entries_count)
694
+ @log.warn "Dropping #{entries_count} log message(s)",
695
+ error_class: error.class.to_s, error: error.to_s
696
+ else
697
+ error_details_map.each do |(error_code, error_message), indexes|
698
+ partial_error_count = indexes.length
699
+ increment_dropped_entries_count(partial_error_count)
700
+ @log.warn "Dropping #{partial_error_count} log message(s)",
701
+ error_code: "google.rpc.Code[#{error_code}]",
702
+ error: error_message
703
+ end
704
+ end
670
705
  end
671
706
  end
672
707
  end
@@ -836,7 +871,7 @@ module Fluent
836
871
  # Metadata Agent. Thus it should be equivalent to what Metadata Agent
837
872
  # returns.
838
873
  def determine_agent_level_monitored_resource_via_legacy
839
- resource = Google::Apis::LoggingV2beta1::MonitoredResource.new(
874
+ resource = Google::Apis::LoggingV2::MonitoredResource.new(
840
875
  labels: {})
841
876
  resource.type = determine_agent_level_monitored_resource_type
842
877
  resource.labels = determine_agent_level_monitored_resource_labels(
@@ -1090,6 +1125,18 @@ module Fluent
1090
1125
  common_labels.delete("#{COMPUTE_CONSTANTS[:service]}/resource_name")
1091
1126
  end
1092
1127
 
1128
+ # Cloud Dataflow and Cloud ML.
1129
+ # These labels can be set via the 'labels' option.
1130
+ # Report them as monitored resource labels instead of common labels.
1131
+ # e.g. "dataflow.googleapis.com/job_id" => "job_id"
1132
+ [DATAFLOW_CONSTANTS, ML_CONSTANTS].each do |service_constants|
1133
+ next unless resource.type == service_constants[:resource_type]
1134
+ resource.labels.merge!(
1135
+ delete_and_extract_labels(
1136
+ common_labels, service_constants[:extra_resource_labels]
1137
+ .map { |l| ["#{service_constants[:service]}/#{l}", l] }.to_h))
1138
+ end
1139
+
1093
1140
  resource.freeze
1094
1141
  resource.labels.freeze
1095
1142
  common_labels.freeze
@@ -1157,14 +1204,14 @@ module Fluent
1157
1204
  common_labels.merge!(delete_and_extract_labels(record, @label_map))
1158
1205
 
1159
1206
  # Cloud Dataflow and Cloud ML.
1160
- # These labels can be set via configuring 'labels' or 'label_map'.
1207
+ # These labels can be set via the 'labels' or 'label_map' options.
1161
1208
  # Report them as monitored resource labels instead of common labels.
1162
1209
  # e.g. "dataflow.googleapis.com/job_id" => "job_id"
1163
1210
  [DATAFLOW_CONSTANTS, ML_CONSTANTS].each do |service_constants|
1164
1211
  next unless resource.type == service_constants[:resource_type]
1165
1212
  resource.labels.merge!(
1166
1213
  delete_and_extract_labels(
1167
- common_labels, service_constants[:extra_common_labels]
1214
+ common_labels, service_constants[:extra_resource_labels]
1168
1215
  .map { |l| ["#{service_constants[:service]}/#{l}", l] }.to_h))
1169
1216
  end
1170
1217
 
@@ -1187,7 +1234,7 @@ module Fluent
1187
1234
  # TODO(qingling128): Use Google::Api::MonitoredResource directly after we
1188
1235
  # upgrade gRPC version to include the fix for the protobuf map
1189
1236
  # corruption issue.
1190
- Google::Apis::LoggingV2beta1::MonitoredResource.new(
1237
+ Google::Apis::LoggingV2::MonitoredResource.new(
1191
1238
  type: resource.type,
1192
1239
  labels: resource.labels.to_h
1193
1240
  )
@@ -1658,7 +1705,7 @@ module Fluent
1658
1705
  # TODO: Use a non-default ClientOptions object.
1659
1706
  Google::Apis::ClientOptions.default.application_name = PLUGIN_NAME
1660
1707
  Google::Apis::ClientOptions.default.application_version = PLUGIN_VERSION
1661
- @client = Google::Apis::LoggingV2beta1::LoggingService.new
1708
+ @client = Google::Apis::LoggingV2::LoggingService.new
1662
1709
  @client.authorization = Google::Auth.get_application_default(
1663
1710
  LOGGING_SCOPE)
1664
1711
  end
@@ -1710,6 +1757,118 @@ module Fluent
1710
1757
  end
1711
1758
  end
1712
1759
 
1760
+ # Extract a map of error details from an potentially partially successful
1761
+ # request. Return an empty map if @partial_success is not enabled.
1762
+ #
1763
+ # The keys in this map are [error_code, error_message] pairs, and the values
1764
+ # are a list of stringified indexes of log entries that failed due to this
1765
+ # error.
1766
+ #
1767
+ # A sample error.body looks like:
1768
+ # {
1769
+ # "error": {
1770
+ # "code": 403,
1771
+ # "message": "User not authorized.",
1772
+ # "status": "PERMISSION_DENIED",
1773
+ # "details": [
1774
+ # {
1775
+ # "@type": "type.googleapis.com/google.logging.v2.WriteLogEntriesPar
1776
+ # tialErrors",
1777
+ # "logEntryErrors": {
1778
+ # "0": {
1779
+ # "code": 7,
1780
+ # "message": "User not authorized."
1781
+ # },
1782
+ # "1": {
1783
+ # "code": 3,
1784
+ # "message": "Log name contains illegal character :"
1785
+ # },
1786
+ # "3": {
1787
+ # "code": 3,
1788
+ # "message": "Log name contains illegal character :"
1789
+ # }
1790
+ # }
1791
+ # },
1792
+ # {
1793
+ # "@type": "type.googleapis.com/google.rpc.DebugInfo",
1794
+ # "detail": ...
1795
+ # }
1796
+ # ]
1797
+ # }
1798
+ # }
1799
+ #
1800
+ # The root level "code", "message", and "status" simply match the root
1801
+ # cause of the first failed log entry. For example, if we switched the order
1802
+ # of the log entries, then we would get:
1803
+ # {
1804
+ # "error" : {
1805
+ # "code" : 400,
1806
+ # "message" : "Log name contains illegal character :",
1807
+ # "status" : "INVALID_ARGUMENT",
1808
+ # "details": ...
1809
+ # }
1810
+ # }
1811
+ # We will ignore it anyway and look at the details instead which includes
1812
+ # info for all failed log entries.
1813
+ #
1814
+ # In this example, the logEntryErrors that we care are:
1815
+ # {
1816
+ # "0": {
1817
+ # "code": 7,
1818
+ # "message": "User not authorized."
1819
+ # },
1820
+ # "1": {
1821
+ # "code": 3,
1822
+ # "message": "Log name contains illegal character :"
1823
+ # },
1824
+ # "3": {
1825
+ # "code": 3,
1826
+ # "message": "Log name contains illegal character :"
1827
+ # }
1828
+ # }
1829
+ #
1830
+ # The ultimate map that is constructed is:
1831
+ # {
1832
+ # [7, 'User not authorized.']: ['0'],
1833
+ # [3, 'Log name contains illegal character :']: ['1', '3']
1834
+ # }
1835
+ def construct_error_details_map(error)
1836
+ return {} unless @partial_success
1837
+ error_details_map = Hash.new { |h, k| h[k] = [] }
1838
+
1839
+ error_details = ensure_array(
1840
+ ensure_hash(ensure_hash(JSON.parse(error.body))['error'])['details'])
1841
+ partial_errors = error_details.detect(
1842
+ -> { fail JSON::ParserError, "No type #{PARTIAL_ERROR_FIELD}." }
1843
+ ) do |error_detail|
1844
+ ensure_hash(error_detail)['@type'] == PARTIAL_ERROR_FIELD
1845
+ end
1846
+ log_entry_errors = ensure_hash(
1847
+ ensure_hash(partial_errors)['logEntryErrors'])
1848
+ log_entry_errors.each do |index, log_entry_error|
1849
+ error_hash = ensure_hash(log_entry_error)
1850
+ fail JSON::ParserError,
1851
+ "Entry #{index} is missing 'code' or 'message'." unless
1852
+ error_hash['code'] && error_hash['message']
1853
+ error_key = [error_hash['code'], error_hash['message']].freeze
1854
+ # TODO(qingling128): Convert indexes to integers.
1855
+ error_details_map[error_key] << index
1856
+ end
1857
+ error_details_map
1858
+ rescue JSON::ParserError => e
1859
+ @log.warn 'Failed to extract log entry errors from the error details:' \
1860
+ " #{error.body}.", error: e
1861
+ {}
1862
+ end
1863
+
1864
+ def ensure_array(value)
1865
+ Array.try_convert(value) || (fail JSON::ParserError, "#{value.class}")
1866
+ end
1867
+
1868
+ def ensure_hash(value)
1869
+ Hash.try_convert(value) || (fail JSON::ParserError, "#{value.class}")
1870
+ end
1871
+
1713
1872
  # Increment the metric for the number of successful requests.
1714
1873
  def increment_successful_requests_count
1715
1874
  return unless @successful_requests_count
@@ -1748,7 +1907,7 @@ end
1748
1907
 
1749
1908
  module Google
1750
1909
  module Apis
1751
- module LoggingV2beta1
1910
+ module LoggingV2
1752
1911
  # Override MonitoredResource::dup to make a deep copy.
1753
1912
  class MonitoredResource
1754
1913
  def dup
@@ -93,6 +93,18 @@ module BaseTest
93
93
  end
94
94
  end
95
95
 
96
+ def test_configure_partial_success
97
+ setup_gce_metadata_stubs
98
+ {
99
+ APPLICATION_DEFAULT_CONFIG => false,
100
+ PARTIAL_SUCCESS_CONFIG => true
101
+ }.each do |(config, partial_success)|
102
+ d = create_driver(config)
103
+ assert_equal partial_success,
104
+ d.instance.instance_variable_get(:@partial_success)
105
+ end
106
+ end
107
+
96
108
  def test_metadata_loading
97
109
  setup_gce_metadata_stubs
98
110
  d = create_driver
@@ -961,6 +973,26 @@ module BaseTest
961
973
  verify_log_entries(1, DATAPROC_PARAMS, 'jsonPayload')
962
974
  end
963
975
 
976
+ def test_cloud_ml_log
977
+ setup_gce_metadata_stubs
978
+ setup_logging_stubs do
979
+ d = create_driver(CONFIG_ML, ML_TAG)
980
+ d.emit(ml_log_entry(0))
981
+ d.run
982
+ end
983
+ verify_log_entries(1, ML_PARAMS)
984
+ end
985
+
986
+ def test_cloud_dataflow_log
987
+ setup_gce_metadata_stubs
988
+ setup_logging_stubs do
989
+ d = create_driver(CONFIG_DATAFLOW, DATAFLOW_TAG)
990
+ d.emit(dataflow_log_entry(0))
991
+ d.run
992
+ end
993
+ verify_log_entries(1, DATAFLOW_PARAMS)
994
+ end
995
+
964
996
  def test_log_entry_http_request_field_from_record
965
997
  verify_subfields_from_record(DEFAULT_HTTP_REQUEST_KEY)
966
998
  end
@@ -1023,11 +1055,11 @@ module BaseTest
1023
1055
  setup_logging_stubs do
1024
1056
  d = create_driver
1025
1057
  @logs_sent = []
1026
- d.emit('httpRequest' => HTTP_REQUEST_MESSAGE.merge('latency' => input))
1058
+ d.emit('httpRequest' => http_request_message.merge('latency' => input))
1027
1059
  d.run
1028
1060
  end
1029
1061
  verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
1030
- assert_equal HTTP_REQUEST_MESSAGE.merge('latency' => expected),
1062
+ assert_equal http_request_message.merge('latency' => expected),
1031
1063
  entry['httpRequest'], entry
1032
1064
  assert_nil get_fields(entry['jsonPayload'])['httpRequest'], entry
1033
1065
  end
@@ -1044,11 +1076,11 @@ module BaseTest
1044
1076
  setup_logging_stubs do
1045
1077
  d = create_driver
1046
1078
  @logs_sent = []
1047
- d.emit('httpRequest' => HTTP_REQUEST_MESSAGE.merge('latency' => input))
1079
+ d.emit('httpRequest' => http_request_message.merge('latency' => input))
1048
1080
  d.run
1049
1081
  end
1050
1082
  verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
1051
- assert_equal HTTP_REQUEST_MESSAGE, entry['httpRequest'], entry
1083
+ assert_equal http_request_message, entry['httpRequest'], entry
1052
1084
  assert_nil get_fields(entry['jsonPayload'])['httpRequest'], entry
1053
1085
  end
1054
1086
  end
@@ -1597,8 +1629,22 @@ module BaseTest
1597
1629
  end
1598
1630
  end
1599
1631
 
1632
+ def log_entry_subfields_params
1633
+ {
1634
+ # The keys are the names of fields in the payload that we are extracting
1635
+ # LogEntry info from. The values are lists of two elements: the name of
1636
+ # the subfield in LogEntry object and the expected value of that field.
1637
+ DEFAULT_HTTP_REQUEST_KEY => [
1638
+ 'httpRequest', http_request_message],
1639
+ DEFAULT_SOURCE_LOCATION_KEY => [
1640
+ 'sourceLocation', source_location_message],
1641
+ DEFAULT_OPERATION_KEY => [
1642
+ 'operation', OPERATION_MESSAGE]
1643
+ }
1644
+ end
1645
+
1600
1646
  def verify_subfields_from_record(payload_key)
1601
- destination_key, payload_value = LOG_ENTRY_SUBFIELDS_PARAMS[payload_key]
1647
+ destination_key, payload_value = log_entry_subfields_params[payload_key]
1602
1648
  @logs_sent = []
1603
1649
  setup_gce_metadata_stubs
1604
1650
  setup_logging_stubs do
@@ -1614,7 +1660,7 @@ module BaseTest
1614
1660
  end
1615
1661
 
1616
1662
  def verify_subfields_partial_from_record(payload_key)
1617
- destination_key, payload_value = LOG_ENTRY_SUBFIELDS_PARAMS[payload_key]
1663
+ destination_key, payload_value = log_entry_subfields_params[payload_key]
1618
1664
  @logs_sent = []
1619
1665
  setup_gce_metadata_stubs
1620
1666
  setup_logging_stubs do
@@ -1631,7 +1677,7 @@ module BaseTest
1631
1677
  end
1632
1678
 
1633
1679
  def verify_subfields_when_not_hash(payload_key)
1634
- destination_key = LOG_ENTRY_SUBFIELDS_PARAMS[payload_key][0]
1680
+ destination_key = log_entry_subfields_params[payload_key][0]
1635
1681
  @logs_sent = []
1636
1682
  setup_gce_metadata_stubs
1637
1683
  setup_logging_stubs do
@@ -1646,14 +1692,22 @@ module BaseTest
1646
1692
  end
1647
1693
  end
1648
1694
 
1695
+ def http_request_message
1696
+ HTTP_REQUEST_MESSAGE
1697
+ end
1698
+
1699
+ def source_location_message
1700
+ SOURCE_LOCATION_MESSAGE
1701
+ end
1702
+
1649
1703
  # Replace the 'referer' field with nil.
1650
1704
  def http_request_message_with_nil_referer
1651
- HTTP_REQUEST_MESSAGE.merge('referer' => nil)
1705
+ http_request_message.merge('referer' => nil)
1652
1706
  end
1653
1707
 
1654
1708
  # Unset the 'referer' field.
1655
1709
  def http_request_message_with_absent_referer
1656
- HTTP_REQUEST_MESSAGE.reject do |k, _|
1710
+ http_request_message.reject do |k, _|
1657
1711
  k == 'referer'
1658
1712
  end
1659
1713
  end
@@ -97,7 +97,7 @@ module Constants
97
97
  DATAFLOW_JOB_NAME = 'job_name_1'
98
98
  DATAFLOW_JOB_ID = 'job_id_1'
99
99
  DATAFLOW_STEP_ID = 'step_1'
100
- DATAFLOW_TAG = 'dataflow.googleapis.com/worker'
100
+ DATAFLOW_TAG = 'dataflow-worker'
101
101
 
102
102
  # Dataproc specific labels.
103
103
  DATAPROC_CLUSTER_NAME = 'test-cluster'
@@ -140,6 +140,10 @@ module Constants
140
140
  detect_json true
141
141
  )
142
142
 
143
+ PARTIAL_SUCCESS_CONFIG = %(
144
+ partial_success true
145
+ )
146
+
143
147
  # rubocop:disable Metrics/LineLength
144
148
  PRIVATE_KEY_CONFIG = %(
145
149
  auth_method private_key
@@ -533,13 +537,6 @@ module Constants
533
537
  'last' => true
534
538
  }
535
539
 
536
- LOG_ENTRY_SUBFIELDS_PARAMS = {
537
- # payload key, destination key, payload value
538
- DEFAULT_HTTP_REQUEST_KEY => ['httpRequest', HTTP_REQUEST_MESSAGE],
539
- DEFAULT_SOURCE_LOCATION_KEY => ['sourceLocation', SOURCE_LOCATION_MESSAGE],
540
- DEFAULT_OPERATION_KEY => ['operation', OPERATION_MESSAGE]
541
- }
542
-
543
540
  CUSTOM_LABELS_MESSAGE = {
544
541
  'customKey' => 'value'
545
542
  }
@@ -605,4 +602,43 @@ module Constants
605
602
  }
606
603
  }.to_json
607
604
  }
605
+
606
+ PARTIAL_SUCCESS_RESPONSE_BODY = {
607
+ 'error' => {
608
+ 'code' => 403,
609
+ 'message' => 'User not authorized.',
610
+ 'status' => 'PERMISSION_DENIED',
611
+ 'details' => [
612
+ {
613
+ '@type' => 'type.googleapis.com/google.logging.v2.WriteLogEntriesPa' \
614
+ 'rtialErrors',
615
+ 'logEntryErrors' => {
616
+ '0' => {
617
+ 'code' => 7,
618
+ 'message' => 'User not authorized.'
619
+ },
620
+ '1' => {
621
+ 'code' => 3,
622
+ 'message' => 'Log name contains illegal character :'
623
+ },
624
+ '2' => {
625
+ 'code' => 3,
626
+ 'message' => 'Log name contains illegal character :'
627
+ }
628
+ }
629
+ },
630
+ {
631
+ '@type' => 'type.googleapis.com/google.rpc.DebugInfo',
632
+ 'detail' => '[ORIGINAL ERROR] generic::permission_denied: User not ' \
633
+ 'authorized. [google.rpc.error_details_ext] { message: \"User not' \
634
+ ' authorized.\" details { type_url: \"type.googleapis.com/google.' \
635
+ 'logging.v2.WriteLogEntriesPartialErrors\" value: \"\\n\\034\\010' \
636
+ '\\000\\022\\030\\010\\007\\022\\024User not authorized.\\n-\\010' \
637
+ '\\001\\022)\\010\\003\\022%Log name contains illegal character :' \
638
+ '\\n-\\010\\002\\022)\\010\\003\\022%Log name contains illegal ch' \
639
+ 'aracter :\" } }'
640
+ }
641
+ ]
642
+ }
643
+ }.to_json
608
644
  end
@@ -51,6 +51,28 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
51
51
  assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 2)
52
52
  end
53
53
 
54
+ def test_partial_success
55
+ setup_gce_metadata_stubs
56
+ {
57
+ APPLICATION_DEFAULT_CONFIG => 4.0,
58
+ PARTIAL_SUCCESS_CONFIG => 3.0
59
+ }.each do |config, failed_entry_count|
60
+ setup_prometheus
61
+ # The API Client should not retry this and the plugin should consume
62
+ # the exception.
63
+ stub_request(:post, WRITE_LOG_ENTRIES_URI)
64
+ .to_return(status: 400, body: PARTIAL_SUCCESS_RESPONSE_BODY)
65
+ d = create_driver(PROMETHEUS_ENABLE_CONFIG + config)
66
+ 4.times do |i|
67
+ d.emit('message' => log_entry(i.to_s))
68
+ end
69
+ d.run
70
+ assert_prometheus_metric_value(:stackdriver_dropped_entries_count,
71
+ failed_entry_count)
72
+ end
73
+ assert_requested(:post, WRITE_LOG_ENTRIES_URI, times: 2)
74
+ end
75
+
54
76
  def test_server_error
55
77
  setup_gce_metadata_stubs
56
78
  # The API client should retry this once, then throw an exception which
@@ -251,7 +273,7 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
251
273
 
252
274
  private
253
275
 
254
- WRITE_LOG_ENTRIES_URI = 'https://logging.googleapis.com/v2beta1/entries:write'
276
+ WRITE_LOG_ENTRIES_URI = 'https://logging.googleapis.com/v2/entries:write'
255
277
 
256
278
  def rename_key(hash, old_key, new_key)
257
279
  hash.merge(new_key => hash[old_key]).reject { |k, _| k == old_key }
@@ -316,4 +338,25 @@ class GoogleCloudOutputTest < Test::Unit::TestCase
316
338
  def null_value
317
339
  nil
318
340
  end
341
+
342
+ # 'responseSize' and 'requestSize' are Integers in the gRPC proto, yet Strings
343
+ # in REST API client.
344
+ # TODO(qingling128): Address this accordingly once the following question is
345
+ # answered: https://github.com/google/google-api-ruby-client/issues/619.
346
+ # If this discrepancy is legit, add some comments to explain the reason.
347
+ # Otherwise once the discrepancy is fixed, we need to upgrade to that version
348
+ # and change our tests accordingly.
349
+ def http_request_message
350
+ HTTP_REQUEST_MESSAGE.merge(
351
+ 'responseSize' => HTTP_REQUEST_MESSAGE['responseSize'].to_s,
352
+ 'requestSize' => HTTP_REQUEST_MESSAGE['requestSize'].to_s
353
+ )
354
+ end
355
+
356
+ # 'line' is an Integer in the gRPC proto, yet a String in the REST API client.
357
+ def source_location_message
358
+ SOURCE_LOCATION_MESSAGE.merge(
359
+ 'line' => SOURCE_LOCATION_MESSAGE['line'].to_s
360
+ )
361
+ end
319
362
  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.6.8
4
+ version: 0.6.9.pre.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Todd Derr
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-09-22 00:00:00.000000000 Z
12
+ date: 2017-10-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fluentd
@@ -45,28 +45,34 @@ dependencies:
45
45
  requirements:
46
46
  - - "~>"
47
47
  - !ruby/object:Gem::Version
48
- version: 0.9.0
48
+ version: '0.14'
49
49
  type: :runtime
50
50
  prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
- version: 0.9.0
55
+ version: '0.14'
56
56
  - !ruby/object:Gem::Dependency
57
57
  name: google-cloud-logging
58
58
  requirement: !ruby/object:Gem::Requirement
59
59
  requirements:
60
- - - '='
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.2'
63
+ - - ">="
61
64
  - !ruby/object:Gem::Version
62
- version: 0.24.1
65
+ version: 1.2.3
63
66
  type: :runtime
64
67
  prerelease: false
65
68
  version_requirements: !ruby/object:Gem::Requirement
66
69
  requirements:
67
- - - '='
70
+ - - "~>"
68
71
  - !ruby/object:Gem::Version
69
- version: 0.24.1
72
+ version: '1.2'
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 1.2.3
70
76
  - !ruby/object:Gem::Dependency
71
77
  name: googleauth
72
78
  requirement: !ruby/object:Gem::Requirement
@@ -249,9 +255,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
249
255
  version: '2.0'
250
256
  required_rubygems_version: !ruby/object:Gem::Requirement
251
257
  requirements:
252
- - - ">="
258
+ - - ">"
253
259
  - !ruby/object:Gem::Version
254
- version: '0'
260
+ version: 1.3.1
255
261
  requirements: []
256
262
  rubyforge_project:
257
263
  rubygems_version: 2.4.8