fluent-plugin-google-cloud 0.8.4 → 0.9.1

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.
@@ -0,0 +1,77 @@
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
+ require 'prometheus/client'
16
+
17
+ # Additional assert functions.
18
+ module Asserts
19
+ # For an optional field with default values, Protobuf omits the field when it
20
+ # is deserialized to json. So we need to add an extra check for gRPC which
21
+ # uses Protobuf.
22
+ #
23
+ # An optional block can be passed in if we need to assert something other than
24
+ # a plain equal. e.g. assert_in_delta.
25
+ def assert_equal_with_default(_field, _expected_value, _default_value, _entry)
26
+ _undefined
27
+ end
28
+
29
+ # Compare the timestamp seconds and nanoseconds with the expected timestamp.
30
+ def assert_timestamp_matches(expected_ts, ts_secs, ts_nanos, entry)
31
+ assert_equal expected_ts.tv_sec, ts_secs, entry
32
+ # Fluentd v0.14 onwards supports nanosecond timestamp values.
33
+ # Added in 600 ns delta to avoid flaky tests introduced
34
+ # due to rounding error in double-precision floating-point numbers
35
+ # (to account for the missing 9 bits of precision ~ 512 ns).
36
+ # See http://wikipedia.org/wiki/Double-precision_floating-point_format.
37
+ assert_in_delta expected_ts.tv_nsec, ts_nanos, 600, entry
38
+ end
39
+
40
+ def assert_prometheus_metric_value(metric_name, expected_value, labels = {})
41
+ metric = Prometheus::Client.registry.get(metric_name)
42
+ assert_not_nil(metric)
43
+ metric_value = if labels == :aggregate
44
+ # Sum up all metric values regardless of the labels.
45
+ metric.values.values.reduce(0.0, :+)
46
+ else
47
+ metric.get(labels)
48
+ end
49
+ assert_equal(expected_value, metric_value)
50
+ end
51
+
52
+ def assert_opencensus_metric_value(metric_name, expected_value, labels = {})
53
+ translator = Monitoring::MetricTranslator.new(metric_name, labels)
54
+ metric_name = translator.name
55
+ labels = translator.translate_labels(labels)
56
+ # The next line collapses the labels to assert against the aggregated data,
57
+ # which can have some labels removed. Without this, it would test against
58
+ # the raw data. The view is more representative of the user experience, even
59
+ # though both tests should work because currently we only aggregate away one
60
+ # label that never changes during runtime.
61
+ labels.select! { |k, _| translator.view_labels.include? k }
62
+ labels = labels.map { |k, v| [k.to_s, v.to_s] }.to_h
63
+ stats_recorder = OpenCensus::Stats.ensure_recorder
64
+ view_data = stats_recorder.view_data metric_name
65
+ assert_not_nil(view_data)
66
+ # For now assume all metrics are counters.
67
+ assert_kind_of(OpenCensus::Stats::Aggregation::Sum,
68
+ view_data.view.aggregation)
69
+ assert_true(view_data.view.measure.int64?)
70
+ tag_values = view_data.view.columns.map { |column| labels[column] }
71
+ metric_value = 0
72
+ if view_data.data.key? tag_values
73
+ metric_value = view_data.data[tag_values].value
74
+ end
75
+ assert_equal(expected_value, metric_value)
76
+ end
77
+ end
@@ -19,10 +19,12 @@ Coveralls.wear!
19
19
  require 'google/apis'
20
20
  require 'helper'
21
21
  require 'mocha/test_unit'
22
- require 'webmock/test_unit'
23
22
  require 'prometheus/client'
23
+ require 'webmock/test_unit'
24
24
 
25
+ require_relative 'asserts'
25
26
  require_relative 'constants'
27
+ require_relative 'utils'
26
28
 
27
29
  module Monitoring
28
30
  # Prevent OpenCensus from writing to the network.
@@ -35,22 +37,13 @@ end
35
37
 
36
38
  # Unit tests for Google Cloud Logging plugin
37
39
  module BaseTest
40
+ include Asserts
38
41
  include Constants
42
+ include Utils
39
43
 
40
44
  def setup
41
45
  Fluent::Test.setup
42
- # delete environment variables that googleauth uses to find credentials.
43
- ENV.delete(CREDENTIALS_PATH_ENV_VAR)
44
- # service account env.
45
- ENV.delete(PRIVATE_KEY_VAR)
46
- ENV.delete(CLIENT_EMAIL_VAR)
47
- ENV.delete(PROJECT_ID_VAR)
48
- # authorized_user env.
49
- ENV.delete(CLIENT_ID_VAR)
50
- ENV.delete(CLIENT_SECRET_VAR)
51
- ENV.delete(REFRESH_TOKEN_VAR)
52
- # home var, which is used to find $HOME/.gcloud/...
53
- ENV.delete('HOME')
46
+ delete_env_vars
54
47
 
55
48
  # Unregister Prometheus metrics.
56
49
  registry = Prometheus::Client.registry
@@ -60,7 +53,7 @@ module BaseTest
60
53
  registry.unregister(:stackdriver_dropped_entries_count)
61
54
  registry.unregister(:stackdriver_retried_entries_count)
62
55
 
63
- setup_auth_stubs
56
+ setup_auth_stubs('https://www.googleapis.com/oauth2/v4/token')
64
57
  @logs_sent = []
65
58
  end
66
59
 
@@ -105,7 +98,7 @@ module BaseTest
105
98
 
106
99
  def test_configure_metadata_missing_parts_on_other_platforms
107
100
  setup_no_metadata_service_stubs
108
- Fluent::GoogleCloudOutput::CredentialsInfo.stubs(:project_id).returns(nil)
101
+ Common::Utils::CredentialsInfo.stubs(:project_id).returns(nil)
109
102
  [[CONFIG_MISSING_METADATA_PROJECT_ID, ['project_id'], false],
110
103
  [CONFIG_MISSING_METADATA_ZONE, [], true],
111
104
  [CONFIG_MISSING_METADATA_VM_ID, [], true],
@@ -129,8 +122,8 @@ module BaseTest
129
122
 
130
123
  def test_configure_ignores_unknown_monitoring_type
131
124
  # Verify that driver creation succeeds when monitoring type is not
132
- # "prometheus" (in which case, we simply don't record metrics),
133
- # and that the counters are set to nil.
125
+ # "prometheus" or "opencensus" (in which case, we simply don't record
126
+ # metrics), and that the counters are set to nil.
134
127
  setup_gce_metadata_stubs
135
128
  create_driver(CONFIG_UNKNOWN_MONITORING_TYPE)
136
129
  assert_nil(Prometheus::Client.registry.get(
@@ -143,6 +136,67 @@ module BaseTest
143
136
  :stackdriver_dropped_entries_count))
144
137
  assert_nil(Prometheus::Client.registry.get(
145
138
  :stackdriver_retried_entries_count))
139
+ assert_nil(OpenCensus::Stats::MeasureRegistry.get(
140
+ Monitoring::MetricTranslator.new(
141
+ :stackdriver_successful_requests_count, {})))
142
+ assert_nil(OpenCensus::Stats::MeasureRegistry.get(
143
+ Monitoring::MetricTranslator.new(
144
+ :stackdriver_failed_requests_count, {})))
145
+ assert_nil(OpenCensus::Stats::MeasureRegistry.get(
146
+ Monitoring::MetricTranslator.new(
147
+ :stackdriver_ingested_entries_count, {})))
148
+ assert_nil(OpenCensus::Stats::MeasureRegistry.get(
149
+ Monitoring::MetricTranslator.new(
150
+ :stackdriver_dropped_entries_count, {})))
151
+ assert_nil(OpenCensus::Stats::MeasureRegistry.get(
152
+ Monitoring::MetricTranslator.new(
153
+ :stackdriver_retried_entries_count, {})))
154
+ end
155
+
156
+ def test_configure_uses_metrics_resource
157
+ setup_gce_metadata_stubs
158
+ [CONFIG_METRICS_RESOURCE_JSON,
159
+ CONFIG_METRICS_RESOURCE_HASH,
160
+ CONFIG_METRICS_RESOURCE_JSON_HASH
161
+ ].each_with_index do |config, index|
162
+ d = create_driver(config)
163
+ assert_equal 'custom_resource', d.instance.monitoring_resource.type, \
164
+ "Index #{index}"
165
+ assert_equal '123', d.instance.monitoring_resource.labels['label1'], \
166
+ "Index #{index}"
167
+ assert_equal 'abc', d.instance.monitoring_resource.labels['label2'], \
168
+ "Index #{index}"
169
+ assert_true d.instance.instance_variable_get(:@enable_monitoring)
170
+ registry = d.instance.instance_variable_get(:@registry)
171
+ assert_not_nil registry
172
+ exporter = registry.instance_variable_get(:@exporter)
173
+ assert_equal 'custom_resource', exporter.resource_type, "Index #{index}"
174
+ assert_equal({ 'label1' => '123', 'label2' => 'abc' },
175
+ exporter.resource_labels, "Index #{index}")
176
+ end
177
+ end
178
+
179
+ def test_configure_metrics_resource_validation
180
+ setup_gce_metadata_stubs
181
+ {
182
+ CONFIG_METRICS_RESOURCE_JSON_NO_TYPE => /type must be a string/,
183
+ CONFIG_METRICS_RESOURCE_JSON_BAD_LABELS => /labels must be a hash/,
184
+ CONFIG_METRICS_RESOURCE_JSON_BAD_KEYS =>
185
+ /unrecognized keys: \[:random\]/,
186
+ CONFIG_METRICS_RESOURCE_JSON_BAD_KEYS_LABELS =>
187
+ /unrecognized keys: \[:"labels\.random"\]/,
188
+ CONFIG_METRICS_RESOURCE_JSON_BAD_KEYS_NO_LABELS =>
189
+ /unrecognized keys: \[:random\]/
190
+ }.each_with_index do |(config, pattern), index|
191
+ begin
192
+ create_driver(config)
193
+ assert false,
194
+ "Invalid config at index #{index} should have raised an error."
195
+ rescue Fluent::ConfigError => error
196
+ assert error.message.match?(pattern), \
197
+ "Index #{index} failed: got #{error.message}."
198
+ end
199
+ end
146
200
  end
147
201
 
148
202
  def test_metadata_loading
@@ -1923,116 +1977,6 @@ module BaseTest
1923
1977
 
1924
1978
  private
1925
1979
 
1926
- def stub_metadata_request(metadata_path, response_body)
1927
- stub_request(:get, 'http://169.254.169.254/computeMetadata/v1/' +
1928
- metadata_path)
1929
- .to_return(body: response_body, status: 200,
1930
- headers: { 'Content-Length' => response_body.length })
1931
- end
1932
-
1933
- def setup_no_metadata_service_stubs
1934
- # Simulate a machine with no metadata service present
1935
- stub_request(:any, %r{http://169.254.169.254/.*})
1936
- .to_raise(Errno::EHOSTUNREACH)
1937
- end
1938
-
1939
- def setup_gce_metadata_stubs
1940
- # Stub the root, used for platform detection by the plugin and 'googleauth'.
1941
- stub_request(:get, 'http://169.254.169.254')
1942
- .to_return(status: 200, headers: { 'Metadata-Flavor' => 'Google' })
1943
-
1944
- # Create stubs for all the GCE metadata lookups the agent needs to make.
1945
- stub_metadata_request('project/project-id', PROJECT_ID)
1946
- stub_metadata_request('instance/zone', FULLY_QUALIFIED_ZONE)
1947
- stub_metadata_request('instance/id', VM_ID)
1948
- stub_metadata_request('instance/attributes/',
1949
- "attribute1\nattribute2\nattribute3")
1950
-
1951
- # Used by 'googleauth' to fetch the default service account credentials.
1952
- stub_request(:get, 'http://169.254.169.254/computeMetadata/v1/' \
1953
- 'instance/service-accounts/default/token')
1954
- .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
1955
- status: 200,
1956
- headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
1957
- 'Content-Type' => 'application/json' })
1958
- end
1959
-
1960
- def setup_ec2_metadata_stubs
1961
- # Stub the root, used for platform detection.
1962
- stub_request(:get, 'http://169.254.169.254')
1963
- .to_return(status: 200, headers: { 'Server' => 'EC2ws' })
1964
-
1965
- # Stub the identity document lookup made by the agent.
1966
- stub_request(:get, 'http://169.254.169.254/latest/dynamic/' \
1967
- 'instance-identity/document')
1968
- .to_return(body: EC2_IDENTITY_DOCUMENT, status: 200,
1969
- headers: { 'Content-Length' => EC2_IDENTITY_DOCUMENT.length })
1970
- end
1971
-
1972
- def setup_auth_stubs
1973
- # Used when loading credentials from a JSON file.
1974
- stub_request(:post, 'https://www.googleapis.com/oauth2/v4/token')
1975
- .with(body: hash_including(grant_type: AUTH_GRANT_TYPE))
1976
- .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
1977
- status: 200,
1978
- headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
1979
- 'Content-Type' => 'application/json' })
1980
-
1981
- stub_request(:post, 'https://www.googleapis.com/oauth2/v4/token')
1982
- .with(body: hash_including(grant_type: 'refresh_token'))
1983
- .to_return(body: %({"access_token": "#{FAKE_AUTH_TOKEN}"}),
1984
- status: 200,
1985
- headers: { 'Content-Length' => FAKE_AUTH_TOKEN.length,
1986
- 'Content-Type' => 'application/json' })
1987
- end
1988
-
1989
- def setup_managed_vm_metadata_stubs
1990
- stub_metadata_request(
1991
- 'instance/attributes/',
1992
- "attribute1\ngae_backend_name\ngae_backend_version\nlast_attribute")
1993
- stub_metadata_request('instance/attributes/gae_backend_name',
1994
- MANAGED_VM_BACKEND_NAME)
1995
- stub_metadata_request('instance/attributes/gae_backend_version',
1996
- MANAGED_VM_BACKEND_VERSION)
1997
- end
1998
-
1999
- def setup_k8s_metadata_stubs(should_respond = true)
2000
- if should_respond
2001
- stub_metadata_request(
2002
- 'instance/attributes/',
2003
- "attribute1\ncluster-location\ncluster-name\nlast_attribute")
2004
- stub_metadata_request('instance/attributes/cluster-location',
2005
- K8S_LOCATION2)
2006
- stub_metadata_request('instance/attributes/cluster-name',
2007
- K8S_CLUSTER_NAME)
2008
- else
2009
- ['cluster-location', 'cluster-name'].each do |metadata_name|
2010
- stub_request(:get, %r{.*instance/attributes/#{metadata_name}.*})
2011
- .to_return(status: 404,
2012
- body: 'The requested URL /computeMetadata/v1/instance/' \
2013
- "attributes/#{metadata_name} was not found on this" \
2014
- ' server.')
2015
- end
2016
- end
2017
- end
2018
-
2019
- def setup_dataproc_metadata_stubs
2020
- stub_metadata_request(
2021
- 'instance/attributes/',
2022
- "attribute1\ndataproc-cluster-uuid\ndataproc-cluster-name")
2023
- stub_metadata_request('instance/attributes/dataproc-cluster-name',
2024
- DATAPROC_CLUSTER_NAME)
2025
- stub_metadata_request('instance/attributes/dataproc-cluster-uuid',
2026
- DATAPROC_CLUSTER_UUID)
2027
- stub_metadata_request('instance/attributes/dataproc-region',
2028
- DATAPROC_REGION)
2029
- end
2030
-
2031
- def clear_metrics
2032
- Prometheus::Client.registry.instance_variable_set('@metrics', {})
2033
- OpenCensus::Stats.ensure_recorder.clear_stats
2034
- end
2035
-
2036
1980
  # Provide a stub context that initializes @logs_sent, executes the block and
2037
1981
  # resets WebMock at the end.
2038
1982
  def new_stub_context
@@ -2534,65 +2478,6 @@ module BaseTest
2534
2478
  _undefined
2535
2479
  end
2536
2480
 
2537
- # For an optional field with default values, Protobuf omits the field when it
2538
- # is deserialized to json. So we need to add an extra check for gRPC which
2539
- # uses Protobuf.
2540
- #
2541
- # An optional block can be passed in if we need to assert something other than
2542
- # a plain equal. e.g. assert_in_delta.
2543
- def assert_equal_with_default(_field, _expected_value, _default_value, _entry)
2544
- _undefined
2545
- end
2546
-
2547
- # Compare the timestamp seconds and nanoseconds with the expected timestamp.
2548
- def assert_timestamp_matches(expected_ts, ts_secs, ts_nanos, entry)
2549
- assert_equal expected_ts.tv_sec, ts_secs, entry
2550
- # Fluentd v0.14 onwards supports nanosecond timestamp values.
2551
- # Added in 600 ns delta to avoid flaky tests introduced
2552
- # due to rounding error in double-precision floating-point numbers
2553
- # (to account for the missing 9 bits of precision ~ 512 ns).
2554
- # See http://wikipedia.org/wiki/Double-precision_floating-point_format.
2555
- assert_in_delta expected_ts.tv_nsec, ts_nanos, 600, entry
2556
- end
2557
-
2558
- def assert_prometheus_metric_value(metric_name, expected_value, labels = {})
2559
- metric = Prometheus::Client.registry.get(metric_name)
2560
- assert_not_nil(metric)
2561
- metric_value = if labels == :aggregate
2562
- # Sum up all metric values regardless of the labels.
2563
- metric.values.values.reduce(0.0, :+)
2564
- else
2565
- metric.get(labels)
2566
- end
2567
- assert_equal(expected_value, metric_value)
2568
- end
2569
-
2570
- def assert_opencensus_metric_value(metric_name, expected_value, labels = {})
2571
- translator = Monitoring::MetricTranslator.new(metric_name, labels)
2572
- metric_name = translator.name
2573
- labels = translator.translate_labels(labels)
2574
- # The next line collapses the labels to assert against the aggregated data,
2575
- # which can have some labels removed. Without this, it would test against
2576
- # the raw data. The view is more representative of the user experience, even
2577
- # though both tests should work because currently we only aggregate away one
2578
- # label that never changes during runtime.
2579
- labels.select! { |k, _| translator.view_labels.include? k }
2580
- labels = labels.map { |k, v| [k.to_s, v.to_s] }.to_h
2581
- stats_recorder = OpenCensus::Stats.ensure_recorder
2582
- view_data = stats_recorder.view_data metric_name
2583
- assert_not_nil(view_data)
2584
- # For now assume all metrics are counters.
2585
- assert_kind_of(OpenCensus::Stats::Aggregation::Sum,
2586
- view_data.view.aggregation)
2587
- assert_true(view_data.view.measure.int64?)
2588
- tag_values = view_data.view.columns.map { |column| labels[column] }
2589
- metric_value = 0
2590
- if view_data.data.key? tag_values
2591
- metric_value = view_data.data[tag_values].value
2592
- end
2593
- assert_equal(expected_value, metric_value)
2594
- end
2595
-
2596
2481
  # Defined in specific gRPC or REST files.
2597
2482
  def expected_operation_message2
2598
2483
  _undefined
@@ -45,7 +45,7 @@ end
45
45
 
46
46
  # Constants used by unit tests for Google Cloud Logging plugin.
47
47
  module Constants
48
- include Fluent::GoogleCloudOutput::ServiceConstants
48
+ include Common::ServiceConstants
49
49
  include Fluent::GoogleCloudOutput::ConfigConstants
50
50
  include Fluent::GoogleCloudOutput::InternalConstants
51
51
 
@@ -433,6 +433,56 @@ module Constants
433
433
  monitoring_type not_prometheus
434
434
  ).freeze
435
435
 
436
+ # rubocop:disable Metrics/LineLength
437
+ CONFIG_METRICS_RESOURCE_JSON = %(
438
+ enable_monitoring true
439
+ monitoring_type opencensus
440
+ metrics_resource {"type":"custom_resource","labels":{"label1":"123","label2":"abc"}}
441
+ ).freeze
442
+
443
+ CONFIG_METRICS_RESOURCE_HASH = %(
444
+ enable_monitoring true
445
+ monitoring_type opencensus
446
+ metrics_resource type:custom_resource, labels.label1:123, labels.label2:abc
447
+ ).freeze
448
+
449
+ CONFIG_METRICS_RESOURCE_JSON_HASH = %(
450
+ enable_monitoring true
451
+ monitoring_type opencensus
452
+ metrics_resource {"type":"custom_resource","labels.label1":"123","labels.label2":"abc"}
453
+ ).freeze
454
+
455
+ CONFIG_METRICS_RESOURCE_JSON_NO_TYPE = %(
456
+ enable_monitoring true
457
+ monitoring_type opencensus
458
+ metrics_resource {"labels":{"label1":"123","label2":"abc"}}
459
+ ).freeze
460
+
461
+ CONFIG_METRICS_RESOURCE_JSON_BAD_LABELS = %(
462
+ enable_monitoring true
463
+ monitoring_type opencensus
464
+ metrics_resource {"type":"custom_resource","labels":"123"}
465
+ ).freeze
466
+
467
+ CONFIG_METRICS_RESOURCE_JSON_BAD_KEYS = %(
468
+ enable_monitoring true
469
+ monitoring_type opencensus
470
+ metrics_resource {"type":"custom_resource","labels":{"label1":"123"},"random":"x"}
471
+ ).freeze
472
+
473
+ CONFIG_METRICS_RESOURCE_JSON_BAD_KEYS_LABELS = %(
474
+ enable_monitoring true
475
+ monitoring_type opencensus
476
+ metrics_resource {"type":"custom_resource","labels":{"label1":"123"},"labels.random":"x"}
477
+ ).freeze
478
+
479
+ CONFIG_METRICS_RESOURCE_JSON_BAD_KEYS_NO_LABELS = %(
480
+ enable_monitoring true
481
+ monitoring_type opencensus
482
+ metrics_resource {"type":"custom_resource","labels.label1":"123","random":"x"}
483
+ ).freeze
484
+ # rubocop:enable Metrics/LineLength
485
+
436
486
  # For statusz.
437
487
  CONFIG_STATUSZ = %(
438
488
  statusz_port 5678
@@ -471,6 +521,22 @@ module Constants
471
521
  zone asia-east2
472
522
  ).freeze
473
523
 
524
+ # For analyze_config.
525
+ CONFIG_ANALYZE_CONFIG_PROMETHEUS = %(
526
+ google_fluentd_config_path \
527
+ test/plugin/data/google-fluentd-custom.conf
528
+ google_fluentd_baseline_config_path \
529
+ test/plugin/data/google-fluentd-baseline.conf
530
+ monitoring_type prometheus
531
+ ).freeze
532
+ CONFIG_ANALYZE_CONFIG_OPENCENSUS = %(
533
+ google_fluentd_config_path \
534
+ test/plugin/data/google-fluentd-custom.conf
535
+ google_fluentd_baseline_config_path \
536
+ test/plugin/data/google-fluentd-baseline.conf
537
+ monitoring_type opencensus
538
+ ).freeze
539
+
474
540
  # Service configurations for various services.
475
541
 
476
542
  # GCE.