fluent-plugin-google-cloud 0.8.4 → 0.9.1

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