fluent-plugin-google-cloud 0.8.2 → 0.8.7

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
  SHA256:
3
- metadata.gz: 574fc6e00d75dccbfc4e23932c57c8ad571f5dad4481f46853c4184b5cf5974d
4
- data.tar.gz: 352d3d7c58e67a3e2c942aa5c676dc1ed3c78c3d3165c873e5e453559ca5409a
3
+ metadata.gz: 851c496e93becdfb6a735f9c71aceecfdeccfff14519ce8311bb3dd42c7ed382
4
+ data.tar.gz: d893b865df5a2b10bac43c65381abe1b95e27333ff77ac45c6cb988c2f446036
5
5
  SHA512:
6
- metadata.gz: 2d55720d7d17e3e981fe16a4f5ffd1b3f7b8c457c372a8a7268e9a42239a9ddcfacc6b6b49561f267dd140484b6ea4995870e2ebd8a48307d1a913842aac2931
7
- data.tar.gz: fad13a8638736db1866f345484f469df9ad2298b79ca1f19f14d74345288edc1f1e9a50cd394a30ae5c46c66cef14ccf45c59e72323497b484806fa88491faf2
6
+ metadata.gz: 6f840e9c06e995be5dd4abd0dcd9d04042c6d78507896fc842ab2b1fb8cb82eac4535fdb6259656cc703e99955e074e64c1ec0292c31d77647cbc95d54e7b919
7
+ data.tar.gz: c1573f562d3691a83b8b3dd2b8e8a0ed1585edac5f67df6179bc8c62af4a3728ce97b90be8827008718921069806eb6e0d2e81fd5c7f6de4c6603a1c2738774e
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fluent-plugin-google-cloud (0.8.2)
4
+ fluent-plugin-google-cloud (0.8.7)
5
5
  fluentd (= 1.7.4)
6
6
  google-api-client (= 0.30.8)
7
7
  google-cloud-logging (= 1.6.6)
@@ -18,7 +18,7 @@ GEM
18
18
  specs:
19
19
  addressable (2.7.0)
20
20
  public_suffix (>= 2.0.2, < 5.0)
21
- ast (2.4.0)
21
+ ast (2.4.1)
22
22
  concurrent-ruby (1.1.6)
23
23
  cool.io (1.6.0)
24
24
  coveralls (0.8.23)
@@ -57,9 +57,9 @@ GEM
57
57
  google-cloud-core (1.5.0)
58
58
  google-cloud-env (~> 1.0)
59
59
  google-cloud-errors (~> 1.0)
60
- google-cloud-env (1.3.1)
60
+ google-cloud-env (1.3.2)
61
61
  faraday (>= 0.17.3, < 2.0)
62
- google-cloud-errors (1.0.0)
62
+ google-cloud-errors (1.0.1)
63
63
  google-cloud-logging (1.6.6)
64
64
  concurrent-ruby (~> 1.1)
65
65
  google-cloud-core (~> 1.2)
@@ -116,14 +116,14 @@ GEM
116
116
  google-cloud-monitoring (~> 0.32)
117
117
  google-cloud-trace (~> 0.35)
118
118
  opencensus (~> 0.5)
119
- os (1.0.1)
120
- parser (2.7.0.4)
119
+ os (1.1.0)
120
+ parser (2.7.1.3)
121
121
  ast (~> 2.4.0)
122
- power_assert (1.1.6)
122
+ power_assert (1.2.0)
123
123
  powerpack (0.1.2)
124
124
  prometheus-client (0.9.0)
125
125
  quantile (~> 0.2.1)
126
- public_suffix (4.0.3)
126
+ public_suffix (4.0.5)
127
127
  quantile (0.2.1)
128
128
  rainbow (2.2.2)
129
129
  rake
@@ -145,7 +145,7 @@ GEM
145
145
  serverengine (2.2.1)
146
146
  sigdump (~> 0.2.2)
147
147
  sigdump (0.2.4)
148
- signet (0.13.0)
148
+ signet (0.14.0)
149
149
  addressable (~> 2.3)
150
150
  faraday (>= 0.17.3, < 2.0)
151
151
  jwt (>= 1.5, < 3.0)
@@ -157,18 +157,18 @@ GEM
157
157
  simplecov-html (0.10.2)
158
158
  stackdriver-core (1.4.0)
159
159
  google-cloud-core (~> 1.2)
160
- strptime (0.2.3)
160
+ strptime (0.2.4)
161
161
  sync (0.5.0)
162
162
  term-ansicolor (1.7.1)
163
163
  tins (~> 1.0)
164
164
  test-unit (3.3.3)
165
165
  power_assert
166
166
  thor (1.0.1)
167
- tins (1.24.1)
167
+ tins (1.25.0)
168
168
  sync
169
- tzinfo (2.0.1)
169
+ tzinfo (2.0.2)
170
170
  concurrent-ruby (~> 1.0)
171
- tzinfo-data (1.2019.3)
171
+ tzinfo-data (1.2020.1)
172
172
  tzinfo (>= 1.0.0)
173
173
  uber (0.1.0)
174
174
  unicode-display_width (1.7.0)
@@ -192,4 +192,4 @@ DEPENDENCIES
192
192
  webmock (= 3.6.2)
193
193
 
194
194
  BUNDLED WITH
195
- 2.1.1
195
+ 2.1.4
@@ -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.8.2'
13
+ gem.version = '0.8.7'
14
14
  gem.authors = ['Stackdriver Agents Team']
15
15
  gem.email = ['stackdriver-agents@google.com']
16
16
  gem.required_ruby_version = Gem::Requirement.new('>= 2.2')
@@ -0,0 +1,381 @@
1
+ # Copyright 2020 Google Inc. All rights reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Common
16
+ # Constants for service names, resource types and etc.
17
+ module ServiceConstants
18
+ APPENGINE_CONSTANTS = {
19
+ service: 'appengine.googleapis.com',
20
+ resource_type: 'gae_app',
21
+ metadata_attributes: %w(gae_backend_name gae_backend_version)
22
+ }.freeze
23
+ COMPUTE_CONSTANTS = {
24
+ service: 'compute.googleapis.com',
25
+ resource_type: 'gce_instance'
26
+ }.freeze
27
+ GKE_CONSTANTS = {
28
+ service: 'container.googleapis.com',
29
+ resource_type: 'gke_container',
30
+ extra_resource_labels: %w(namespace_id pod_id container_name),
31
+ extra_common_labels: %w(namespace_name pod_name),
32
+ metadata_attributes: %w(cluster-name cluster-location),
33
+ stream_severity_map: {
34
+ 'stdout' => 'INFO',
35
+ 'stderr' => 'ERROR'
36
+ }
37
+ }.freeze
38
+ K8S_CONTAINER_CONSTANTS = {
39
+ resource_type: 'k8s_container'
40
+ }.freeze
41
+ K8S_POD_CONSTANTS = {
42
+ resource_type: 'k8s_pod'
43
+ }.freeze
44
+ K8S_NODE_CONSTANTS = {
45
+ resource_type: 'k8s_node'
46
+ }.freeze
47
+ DATAFLOW_CONSTANTS = {
48
+ service: 'dataflow.googleapis.com',
49
+ resource_type: 'dataflow_step',
50
+ extra_resource_labels: %w(region job_name job_id step_id)
51
+ }.freeze
52
+ DATAPROC_CONSTANTS = {
53
+ service: 'cluster.dataproc.googleapis.com',
54
+ resource_type: 'cloud_dataproc_cluster',
55
+ metadata_attributes: %w(dataproc-cluster-uuid dataproc-cluster-name)
56
+ }.freeze
57
+ EC2_CONSTANTS = {
58
+ service: 'ec2.amazonaws.com',
59
+ resource_type: 'aws_ec2_instance'
60
+ }.freeze
61
+ ML_CONSTANTS = {
62
+ service: 'ml.googleapis.com',
63
+ resource_type: 'ml_job',
64
+ extra_resource_labels: %w(job_id task_name)
65
+ }.freeze
66
+
67
+ # The map between a subservice name and a resource type.
68
+ SUBSERVICE_MAP =
69
+ [APPENGINE_CONSTANTS, GKE_CONSTANTS, DATAFLOW_CONSTANTS,
70
+ DATAPROC_CONSTANTS, ML_CONSTANTS]
71
+ .map { |consts| [consts[:service], consts[:resource_type]] }.to_h
72
+ # Default back to GCE if invalid value is detected.
73
+ SUBSERVICE_MAP.default = COMPUTE_CONSTANTS[:resource_type]
74
+ SUBSERVICE_MAP.freeze
75
+
76
+ # The map between a resource type and expected subservice attributes.
77
+ SUBSERVICE_METADATA_ATTRIBUTES =
78
+ [APPENGINE_CONSTANTS, GKE_CONSTANTS, DATAPROC_CONSTANTS].map do |consts|
79
+ [consts[:resource_type], consts[:metadata_attributes].to_set]
80
+ end.to_h.freeze
81
+ end
82
+
83
+ # Name of the the Google cloud logging write scope.
84
+ LOGGING_SCOPE = 'https://www.googleapis.com/auth/logging.write'.freeze
85
+
86
+ # Address of the metadata service.
87
+ METADATA_SERVICE_ADDR = '169.254.169.254'.freeze
88
+
89
+ # "enum" of Platform values
90
+ module Platform
91
+ OTHER = 0 # Other/unkown platform
92
+ GCE = 1 # Google Compute Engine
93
+ EC2 = 2 # Amazon EC2
94
+ end
95
+
96
+ # Utilities for managing the resource used when writing to the
97
+ # Google API.
98
+ class Utils
99
+ include Common::ServiceConstants
100
+
101
+ def initialize(log)
102
+ @log = log
103
+ end
104
+
105
+ # Determine what platform we are running on by consulting the metadata
106
+ # service (unless the user has explicitly disabled using that).
107
+ def detect_platform(use_metadata_service)
108
+ unless use_metadata_service
109
+ @log.info 'use_metadata_service is false; not detecting platform'
110
+ return Platform::OTHER
111
+ end
112
+
113
+ begin
114
+ open('http://' + METADATA_SERVICE_ADDR, proxy: false) do |f|
115
+ if f.meta['metadata-flavor'] == 'Google'
116
+ @log.info 'Detected GCE platform'
117
+ return Platform::GCE
118
+ end
119
+ if f.meta['server'] == 'EC2ws'
120
+ @log.info 'Detected EC2 platform'
121
+ return Platform::EC2
122
+ end
123
+ end
124
+ rescue StandardError => e
125
+ @log.error 'Failed to access metadata service: ', error: e
126
+ end
127
+
128
+ @log.info 'Unable to determine platform'
129
+ Platform::OTHER
130
+ end
131
+
132
+ def fetch_gce_metadata(platform, metadata_path)
133
+ raise "Called fetch_gce_metadata with platform=#{platform}" unless
134
+ platform == Platform::GCE
135
+ # See https://cloud.google.com/compute/docs/metadata
136
+ open('http://' + METADATA_SERVICE_ADDR + '/computeMetadata/v1/' +
137
+ metadata_path, 'Metadata-Flavor' => 'Google', :proxy => false,
138
+ &:read)
139
+ end
140
+
141
+ # EC2 Metadata server returns everything in one call. Store it after the
142
+ # first fetch to avoid making multiple calls.
143
+ def ec2_metadata(platform)
144
+ raise "Called ec2_metadata with platform=#{platform}" unless
145
+ platform == Platform::EC2
146
+ unless @ec2_metadata
147
+ # See http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html
148
+ open('http://' + METADATA_SERVICE_ADDR +
149
+ '/latest/dynamic/instance-identity/document', proxy: false) do |f|
150
+ contents = f.read
151
+ @ec2_metadata = JSON.parse(contents)
152
+ end
153
+ end
154
+
155
+ @ec2_metadata
156
+ end
157
+
158
+ # Check required variables like @project_id, @vm_id, @vm_name and @zone.
159
+ def check_required_metadata_variables(platform, project_id, zone, vm_id)
160
+ missing = []
161
+ missing << 'project_id' unless project_id
162
+ if platform != Platform::OTHER
163
+ missing << 'zone' unless zone
164
+ missing << 'vm_id' unless vm_id
165
+ end
166
+ return if missing.empty?
167
+ raise Fluent::ConfigError,
168
+ "Unable to obtain metadata parameters: #{missing.join(' ')}"
169
+ end
170
+
171
+ # 1. Return the value if it is explicitly set in the config already.
172
+ # 2. If not, try to retrieve it by calling metadata server directly.
173
+ # 3. If still not set, try to obtain it from the credentials.
174
+ def get_project_id(platform, project_id)
175
+ project_id ||= CredentialsInfo.project_id
176
+ project_id ||= fetch_gce_metadata(platform, 'project/project-id') if
177
+ platform == Platform::GCE
178
+ project_id
179
+ end
180
+
181
+ # 1. Return the value if it is explicitly set in the config already.
182
+ # 2. If not, try to retrieve it by calling metadata servers directly.
183
+ def get_vm_id(platform, vm_id)
184
+ vm_id ||= fetch_gce_metadata(platform, 'instance/id') if
185
+ platform == Platform::GCE
186
+ vm_id ||= ec2_metadata(platform)['instanceId'] if
187
+ platform == Platform::EC2
188
+ vm_id
189
+ rescue StandardError => e
190
+ @log.error 'Failed to obtain vm_id: ', error: e
191
+ end
192
+
193
+ # 1. Return the value if it is explicitly set in the config already.
194
+ # 2. If not, try to retrieve it locally.
195
+ def get_vm_name(vm_name)
196
+ vm_name ||= Socket.gethostname
197
+ vm_name
198
+ rescue StandardError => e
199
+ @log.error 'Failed to obtain vm name: ', error: e
200
+ end
201
+
202
+ # 1. Return the value if it is explicitly set in the config already.
203
+ # 2. If not, try to retrieve it locally.
204
+ def get_location(platform, zone, use_aws_availability_zone)
205
+ # Response format: "projects/<number>/zones/<zone>"
206
+ zone ||= fetch_gce_metadata(platform,
207
+ 'instance/zone').rpartition('/')[2] if
208
+ platform == Platform::GCE
209
+ aws_location_key = if use_aws_availability_zone
210
+ 'availabilityZone'
211
+ else
212
+ 'region'
213
+ end
214
+ zone ||= 'aws:' + ec2_metadata(platform)[aws_location_key] if
215
+ platform == Platform::EC2 &&
216
+ ec2_metadata(platform).key?(aws_location_key)
217
+ zone
218
+ rescue StandardError => e
219
+ @log.error 'Failed to obtain location: ', error: e
220
+ end
221
+
222
+ # Retrieve monitored resource via the legacy way.
223
+ #
224
+ # Note: This is just a failover plan if we fail to get metadata from
225
+ # Metadata Agent. Thus it should be equivalent to what Metadata Agent
226
+ # returns.
227
+ def determine_agent_level_monitored_resource_via_legacy(
228
+ platform, subservice_name, detect_subservice, vm_id, zone)
229
+ resource = Google::Apis::LoggingV2::MonitoredResource.new(
230
+ labels: {})
231
+ resource.type = determine_agent_level_monitored_resource_type(
232
+ platform, subservice_name, detect_subservice)
233
+ resource.labels = determine_agent_level_monitored_resource_labels(
234
+ platform, resource.type, vm_id, zone)
235
+ resource
236
+ end
237
+
238
+ # Determine agent level monitored resource type.
239
+ def determine_agent_level_monitored_resource_type(
240
+ platform, subservice_name, detect_subservice)
241
+ case platform
242
+ when Platform::OTHER
243
+ # Unknown platform will be defaulted to GCE instance.
244
+ return COMPUTE_CONSTANTS[:resource_type]
245
+
246
+ when Platform::EC2
247
+ return EC2_CONSTANTS[:resource_type]
248
+
249
+ when Platform::GCE
250
+ # Resource types determined by subservice_name config.
251
+ return SUBSERVICE_MAP[subservice_name] if subservice_name
252
+
253
+ # Resource types determined by detect_subservice config.
254
+ if detect_subservice
255
+ begin
256
+ attributes = fetch_gce_metadata(platform,
257
+ 'instance/attributes/').split.to_set
258
+ SUBSERVICE_METADATA_ATTRIBUTES.each do |resource_type, expected|
259
+ return resource_type if attributes.superset?(expected)
260
+ end
261
+ rescue StandardError => e
262
+ @log.error 'Failed to detect subservice: ', error: e
263
+ end
264
+ end
265
+
266
+ # GCE instance.
267
+ return COMPUTE_CONSTANTS[:resource_type]
268
+ end
269
+ end
270
+
271
+ # Determine agent level monitored resource labels based on the resource
272
+ # type. Each resource type has its own labels that need to be filled in.
273
+ def determine_agent_level_monitored_resource_labels(
274
+ platform, type, vm_id, zone)
275
+ case type
276
+ # GAE app.
277
+ when APPENGINE_CONSTANTS[:resource_type]
278
+ return {
279
+ 'module_id' =>
280
+ fetch_gce_metadata(platform,
281
+ 'instance/attributes/gae_backend_name'),
282
+ 'version_id' =>
283
+ fetch_gce_metadata(platform,
284
+ 'instance/attributes/gae_backend_version')
285
+ }
286
+
287
+ # GCE.
288
+ when COMPUTE_CONSTANTS[:resource_type]
289
+ raise "Cannot construct a #{type} resource without vm_id and zone" \
290
+ unless vm_id && zone
291
+ return {
292
+ 'instance_id' => vm_id,
293
+ 'zone' => zone
294
+ }
295
+
296
+ # GKE container.
297
+ when GKE_CONSTANTS[:resource_type]
298
+ raise "Cannot construct a #{type} resource without vm_id and zone" \
299
+ unless vm_id && zone
300
+ return {
301
+ 'instance_id' => vm_id,
302
+ 'zone' => zone,
303
+ 'cluster_name' =>
304
+ fetch_gce_metadata(platform, 'instance/attributes/cluster-name')
305
+ }
306
+
307
+ # Cloud Dataproc.
308
+ when DATAPROC_CONSTANTS[:resource_type]
309
+ return {
310
+ 'cluster_uuid' =>
311
+ fetch_gce_metadata(platform,
312
+ 'instance/attributes/dataproc-cluster-uuid'),
313
+ 'cluster_name' =>
314
+ fetch_gce_metadata(platform,
315
+ 'instance/attributes/dataproc-cluster-name'),
316
+ 'region' =>
317
+ fetch_gce_metadata(platform,
318
+ 'instance/attributes/dataproc-region')
319
+ }
320
+
321
+ # EC2.
322
+ when EC2_CONSTANTS[:resource_type]
323
+ raise "Cannot construct a #{type} resource without vm_id and zone" \
324
+ unless vm_id && zone
325
+ labels = {
326
+ 'instance_id' => vm_id,
327
+ 'region' => zone
328
+ }
329
+ labels['aws_account'] = ec2_metadata(platform)['accountId'] if
330
+ ec2_metadata(platform).key?('accountId')
331
+ return labels
332
+ end
333
+
334
+ {}
335
+ rescue StandardError => e
336
+ if [Platform::GCE, Platform::EC2].include?(platform)
337
+ @log.error "Failed to set monitored resource labels for #{type}: ",
338
+ error: e
339
+ end
340
+ {}
341
+ end
342
+
343
+ # TODO: This functionality should eventually be available in another
344
+ # library, but implement it ourselves for now.
345
+ module CredentialsInfo
346
+ # Determine the project ID from the credentials, if possible.
347
+ # Returns the project ID (as a string) on success, or nil on failure.
348
+ def self.project_id
349
+ creds = Google::Auth.get_application_default(LOGGING_SCOPE)
350
+ if creds.respond_to?(:project_id)
351
+ return creds.project_id if creds.project_id
352
+ end
353
+ if creds.issuer
354
+ id = extract_project_id(creds.issuer)
355
+ return id unless id.nil?
356
+ end
357
+ if creds.client_id
358
+ id = extract_project_id(creds.client_id)
359
+ return id unless id.nil?
360
+ end
361
+ nil
362
+ end
363
+
364
+ # Extracts the project id (either name or number) from str and returns
365
+ # it (as a string) on success, or nil on failure.
366
+ #
367
+ # Recognizes IAM format (account@project-name.iam.gserviceaccount.com)
368
+ # as well as the legacy format with a project number at the front of the
369
+ # string, terminated by a dash (-) which is not part of the ID, i.e.:
370
+ # <PROJECT_ID>-<OTHER_PARTS>.apps.googleusercontent.com
371
+ def self.extract_project_id(str)
372
+ [/^.*@(?<project_id>.+)\.iam\.gserviceaccount\.com/,
373
+ /^(?<project_id>\d+)-/].each do |exp|
374
+ match_data = exp.match(str)
375
+ return match_data['project_id'] unless match_data.nil?
376
+ end
377
+ nil
378
+ end
379
+ end
380
+ end
381
+ end