vmik-fluent-plugin-google-cloud 0.5.5.alpha1

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,7 @@
1
+ {
2
+ "private_key_id": "cbedb7568906086cab57859bbfc1748749cc46c4",
3
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIICdwIBADANBgkqhkiG9w0BAQEcAASCAmEwggJdAgEAAoGBAKizy6B+aJ0Wua0e\njZ3pkHV0a2Ce1prJGhzGL5NpkbUjk6J11Kwp1yvPikTwALyy4PtUIZ+23D/unVRM\nHlKa2MkHIGjJg+mykX5Bd7eRJOxdJ0iu+eRWh7HiH+mdDntHwaz4xXihJBog71qS\n+9N+r2hy1hicybechchMiXHhmWPbAgMBAAECgYEAnSzeI4qCZxEcLtnPcXeBWpz7\nycpTAWUpycMvsjTiRxR9YRhM65YT3cJ//VhqJ2S1ThOcPCt/KqViuX4tpiKUo7qA\nH1AI9APbTo66wiGpgy+qG0wPJkKIQC8PpITNNcHqcbbAsIr3/XQduihsqxP2W2mT\na0nk5XJghs1Wa0xt28ECQQDgMqZjVDcDQyqM+bcBKJUUc/247KusjpdK70r6sx2o\nkZJGy/w9exlM5QrB6DLpw34/p5x4MoecZ7lS3yHdmaEhAkEAwKHsV4k5SXTUp4+J\nWK6GlQVvnwc+PQdX5gzt4/gWSY0Op5EQ+YD6cC7Lkz+GzXUzvmdp35c0ahS93D1/\nZLTZewJBAIjOc3cHMNadyr5BtulPEUE0ro+EY/GlBS8lu/QlDmkJg2AOI3qEvliM\nvza58S9yKny/U5yJAPVw2cZ3ABxQHeECQDyBX8PrBURuXvE2o5RoVTtvlqziAi3X\nJaPLwdkOLqnxlX3KkgNcoM0l1amtlYDpZcRVcSs0+9TqKOyJoH8YUwsCQA4cJmv3\n119xcijXPM2HZOB5cCxTHj59MRtQlLboNZ2witDCJ20eG9AC3ZcH7csS0H9dz8Jr\nXGEoQMPD2ck4T0U\u003d\n-----END PRIVATE KEY-----\n",
4
+ "client_email": "847859579879-q8ancssppuvtv8dac0i742pslde81jgl@developer.gserviceaccount.com",
5
+ "client_id": "847859579879-q8ancssppuvtv8dac0i742pslde81jgl.apps.googleusercontent.com",
6
+ "type": "service_account"
7
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "type": "service_account",
3
+ "private_key_id": "5985985bcdfe958895bd8d76456fe90d8484789d",
4
+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIICdwIBADANBgkqhkiG9w0BAQEcAASCAmEwggJdAgEAAoGBAKizy6B+aJ0Wua0e\njZ3pkHV0a2Ce1prJGhzGL5NpkbUjk6J11Kwp1yvPikTwALyy4PtUIZ+23D/unVRM\nHlKa2MkHIGjJg+mykX5Bd7eRJOxdJ0iu+eRWh7HiH+mdDntHwaz4xXihJBog71qS\n+9N+r2hy1hicybechchMiXHhmWPbAgMBAAECgYEAnSzeI4qCZxEcLtnPcXeBWpz7\nycpTAWUpycMvsjTiRxR9YRhM65YT3cJ//VhqJ2S1ThOcPCt/KqViuX4tpiKUo7qA\nH1AI9APbTo66wiGpgy+qG0wPJkKIQC8PpITNNcHqcbbAsIr3/XQduihsqxP2W2mT\na0nk5XJghs1Wa0xt28ECQQDgMqZjVDcDQyqM+bcBKJUUc/247KusjpdK70r6sx2o\nkZJGy/w9exlM5QrB6DLpw34/p5x4MoecZ7lS3yHdmaEhAkEAwKHsV4k5SXTUp4+J\nWK6GlQVvnwc+PQdX5gzt4/gWSY0Op5EQ+YD6cC7Lkz+GzXUzvmdp35c0ahS93D1/\nZLTZewJBAIjOc3cHMNadyr5BtulPEUE0ro+EY/GlBS8lu/QlDmkJg2AOI3qEvliM\nvza58S9yKny/U5yJAPVw2cZ3ABxQHeECQDyBX8PrBURuXvE2o5RoVTtvlqziAi3X\nJaPLwdkOLqnxlX3KkgNcoM0l1amtlYDpZcRVcSs0+9TqKOyJoH8YUwsCQA4cJmv3\n119xcijXPM2HZOB5cCxTHj59MRtQlLboNZ2witDCJ20eG9AC3ZcH7csS0H9dz8Jr\nXGEoQMPD2ck4T0U\u003d\n-----END PRIVATE KEY-----\n",
5
+ "client_email": "account-name@fluent-test-project.iam.gserviceaccount.com",
6
+ "client_id": "275859789789367827863",
7
+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
8
+ "token_uri": "https://accounts.google.com/o/oauth2/token",
9
+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
10
+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/account-name%40fluent-test-project.iam.gserviceaccount.com"
11
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "private_key_id": "cbedb7568906086cab57859bbfc1748749cc46c4",
3
+ "private_key": "-----BEGIN PRIVATE KEY-----\nCeci n'est pas une cle\n-----END PRIVATE KEY-----\n",
4
+ "client_email": "847859579879-q8ancssppuvtv8dac0i742pslde81jgl@developer.gserviceaccount.com",
5
+ "client_id": "847859579879-q8ancssppuvtv8dac0i742pslde81jgl.apps.googleusercontent.com",
6
+ "type": "service_account"
7
+ }
8
+
@@ -0,0 +1,297 @@
1
+ # Copyright 2014 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_relative 'base_test'
16
+
17
+ # Unit tests for Google Cloud Logging plugin
18
+ class GoogleCloudOutputTest < Test::Unit::TestCase
19
+ include BaseTest
20
+
21
+ def test_configure_use_grpc
22
+ setup_gce_metadata_stubs
23
+ d = create_driver
24
+ assert_false d.instance.instance_variable_get(:@use_grpc)
25
+ end
26
+
27
+ def test_client_400
28
+ setup_gce_metadata_stubs
29
+ # The API Client should not retry this and the plugin should consume
30
+ # the exception.
31
+ stub_request(:post, uri_for_log(COMPUTE_PARAMS))
32
+ .to_return(status: 400, body: 'Bad Request')
33
+ d = create_driver
34
+ d.emit('message' => log_entry(0))
35
+ d.run
36
+ assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 1)
37
+ end
38
+
39
+ # All credentials errors resolve to a 401.
40
+ def test_client_401
41
+ setup_gce_metadata_stubs
42
+ stub_request(:post, uri_for_log(COMPUTE_PARAMS))
43
+ .to_return(status: 401, body: 'Unauthorized')
44
+ d = create_driver
45
+ d.emit('message' => log_entry(0))
46
+ begin
47
+ d.run
48
+ rescue Google::Apis::AuthorizationError => error
49
+ assert_equal 'Unauthorized', error.message
50
+ end
51
+ assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 2)
52
+ end
53
+
54
+ def test_server_error
55
+ setup_gce_metadata_stubs
56
+ # The API client should retry this once, then throw an exception which
57
+ # gets propagated through the plugin.
58
+ stub_request(:post, uri_for_log(COMPUTE_PARAMS))
59
+ .to_return(status: 500, body: 'Server Error')
60
+ d = create_driver
61
+ d.emit('message' => log_entry(0))
62
+ exception_count = 0
63
+ begin
64
+ d.run
65
+ rescue Google::Apis::ServerError => error
66
+ assert_equal 'Server error', error.message
67
+ exception_count += 1
68
+ end
69
+ assert_requested(:post, uri_for_log(COMPUTE_PARAMS), times: 1)
70
+ assert_equal 1, exception_count
71
+ end
72
+
73
+ def test_http_request_from_record_with_referer_nil
74
+ setup_gce_metadata_stubs
75
+ setup_logging_stubs do
76
+ d = create_driver
77
+ d.emit('httpRequest' => http_request_message_with_nil_referer)
78
+ d.run
79
+ end
80
+ verify_log_entries(1, COMPUTE_PARAMS, 'httpRequest') do |entry|
81
+ # The request we send to Logging API has json like:
82
+ # "httpRequest": { "referer": null }, but eventually the stored LogEntry
83
+ # would be "httpRequest": {}, since 'referer' is defined as a string in
84
+ # the proto.
85
+ assert_equal http_request_message_with_nil_referer,
86
+ entry['httpRequest'], entry
87
+ assert_nil get_fields(entry['structPayload'])['httpRequest'], entry
88
+ end
89
+ end
90
+
91
+ # This test looks similar between the grpc and non-grpc paths except that when
92
+ # parsing "105", the grpc path responds with "DEBUG", while the non-grpc path
93
+ # responds with "100".
94
+ #
95
+ # TODO(lingshi) consolidate the tests between the grpc path and the non-grpc
96
+ # path, or at least split into two tests, one with string severities and one
97
+ # with numeric severities.
98
+ def test_severities
99
+ setup_gce_metadata_stubs
100
+ expected_severity = []
101
+ emit_index = 0
102
+ setup_logging_stubs do
103
+ d = create_driver
104
+ # Array of pairs of [parsed_severity, expected_severity]
105
+ [%w(INFO INFO), %w(warn WARNING), %w(E ERROR), %w(BLAH DEFAULT),
106
+ ['105', 100], ['', 'DEFAULT']].each do |sev|
107
+ d.emit('message' => log_entry(emit_index), 'severity' => sev[0])
108
+ expected_severity.push(sev[1])
109
+ emit_index += 1
110
+ end
111
+ d.run
112
+ end
113
+ verify_index = 0
114
+ verify_log_entries(emit_index, COMPUTE_PARAMS) do |entry|
115
+ assert_equal expected_severity[verify_index],
116
+ entry['metadata']['severity'], entry
117
+ verify_index += 1
118
+ end
119
+ end
120
+
121
+ def test_parse_severity
122
+ test_obj = Fluent::GoogleCloudOutput.new
123
+
124
+ # known severities should translate to themselves, regardless of case
125
+ %w(DEFAULT DEBUG INFO NOTICE WARNING ERROR CRITICAL ALERT EMERGENCY).each \
126
+ do |severity|
127
+ assert_equal(severity, test_obj.parse_severity(severity))
128
+ assert_equal(severity, test_obj.parse_severity(severity.downcase))
129
+ assert_equal(severity, test_obj.parse_severity(severity.capitalize))
130
+ end
131
+
132
+ # numeric levels
133
+ assert_equal(0, test_obj.parse_severity('0'))
134
+ assert_equal(100, test_obj.parse_severity('100'))
135
+ assert_equal(200, test_obj.parse_severity('200'))
136
+ assert_equal(300, test_obj.parse_severity('300'))
137
+ assert_equal(400, test_obj.parse_severity('400'))
138
+ assert_equal(500, test_obj.parse_severity('500'))
139
+ assert_equal(600, test_obj.parse_severity('600'))
140
+ assert_equal(700, test_obj.parse_severity('700'))
141
+ assert_equal(800, test_obj.parse_severity('800'))
142
+
143
+ assert_equal(800, test_obj.parse_severity('900'))
144
+ assert_equal(0, test_obj.parse_severity('1'))
145
+ assert_equal(100, test_obj.parse_severity('105'))
146
+ assert_equal(400, test_obj.parse_severity('420'))
147
+ assert_equal(700, test_obj.parse_severity('799'))
148
+
149
+ assert_equal(100, test_obj.parse_severity('105 '))
150
+ assert_equal(100, test_obj.parse_severity(' 105'))
151
+ assert_equal(100, test_obj.parse_severity(' 105 '))
152
+
153
+ assert_equal('DEFAULT', test_obj.parse_severity('-100'))
154
+ assert_equal('DEFAULT', test_obj.parse_severity('105 100'))
155
+
156
+ # synonyms for existing log levels
157
+ assert_equal('ERROR', test_obj.parse_severity('ERR'))
158
+ assert_equal('WARNING', test_obj.parse_severity('WARN'))
159
+ assert_equal('CRITICAL', test_obj.parse_severity('FATAL'))
160
+ assert_equal('DEBUG', test_obj.parse_severity('TRACE'))
161
+ assert_equal('DEBUG', test_obj.parse_severity('TRACE_INT'))
162
+ assert_equal('DEBUG', test_obj.parse_severity('FINE'))
163
+ assert_equal('DEBUG', test_obj.parse_severity('FINER'))
164
+ assert_equal('DEBUG', test_obj.parse_severity('FINEST'))
165
+
166
+ # single letters.
167
+ assert_equal('DEBUG', test_obj.parse_severity('D'))
168
+ assert_equal('INFO', test_obj.parse_severity('I'))
169
+ assert_equal('NOTICE', test_obj.parse_severity('N'))
170
+ assert_equal('WARNING', test_obj.parse_severity('W'))
171
+ assert_equal('ERROR', test_obj.parse_severity('E'))
172
+ assert_equal('CRITICAL', test_obj.parse_severity('C'))
173
+ assert_equal('ALERT', test_obj.parse_severity('A'))
174
+ assert_equal('ERROR', test_obj.parse_severity('e'))
175
+
176
+ assert_equal('DEFAULT', test_obj.parse_severity('x'))
177
+ assert_equal('DEFAULT', test_obj.parse_severity('-'))
178
+
179
+ # leading/trailing whitespace should be stripped
180
+ assert_equal('ERROR', test_obj.parse_severity(' ERROR'))
181
+ assert_equal('ERROR', test_obj.parse_severity('ERROR '))
182
+ assert_equal('ERROR', test_obj.parse_severity(' ERROR '))
183
+ assert_equal('ERROR', test_obj.parse_severity("\t ERROR "))
184
+
185
+ # space in the middle should not be stripped.
186
+ assert_equal('DEFAULT', test_obj.parse_severity('ER ROR'))
187
+
188
+ # anything else should translate to 'DEFAULT'
189
+ assert_equal('DEFAULT', test_obj.parse_severity(''))
190
+ assert_equal('DEFAULT', test_obj.parse_severity('garbage'))
191
+ assert_equal('DEFAULT', test_obj.parse_severity('er'))
192
+ end
193
+
194
+ def test_non_integer_timestamp
195
+ setup_gce_metadata_stubs
196
+ time = Time.now
197
+ [
198
+ { 'seconds' => nil, 'nanos' => nil },
199
+ { 'seconds' => nil, 'nanos' => time.tv_nsec },
200
+ { 'seconds' => 'seconds', 'nanos' => time.tv_nsec },
201
+ { 'seconds' => time.tv_sec, 'nanos' => 'nanos' },
202
+ { 'seconds' => time.tv_sec, 'nanos' => nil }
203
+ ].each do |timestamp|
204
+ setup_logging_stubs do
205
+ d = create_driver
206
+ @logs_sent = []
207
+ d.emit('message' => log_entry(0), 'timestamp' => timestamp)
208
+ d.run
209
+ end
210
+ verify_log_entries(1, COMPUTE_PARAMS) do |entry|
211
+ assert_equal timestamp, entry['metadata']['timestamp'],
212
+ "Test with timestamp '#{timestamp}' failed for " \
213
+ "entry: '#{entry}'."
214
+ end
215
+ end
216
+ end
217
+
218
+ private
219
+
220
+ def rename_key(hash, old_key, new_key)
221
+ hash.merge(new_key => hash[old_key]).reject { |k, _| k == old_key }
222
+ end
223
+
224
+ # The REST path uses old bindings that were generated prior to the field
225
+ # rename, and has to use the old name, which is 'validatedWithOriginServer'.
226
+ def http_request_message
227
+ rename_key(super, 'cacheValidatedWithOriginServer',
228
+ 'validatedWithOriginServer')
229
+ end
230
+
231
+ # Set up http stubs to mock the external calls.
232
+ def setup_logging_stubs(override_stub_params = nil)
233
+ stub_params = override_stub_params || \
234
+ [COMPUTE_PARAMS, VMENGINE_PARAMS, CONTAINER_FROM_TAG_PARAMS,
235
+ CONTAINER_FROM_METADATA_PARAMS, CLOUDFUNCTIONS_PARAMS,
236
+ CUSTOM_PARAMS, EC2_PARAMS]
237
+ stub_params.each do |params|
238
+ stub_request(:post, uri_for_log(params)).to_return do |request|
239
+ log_name = "projects/#{params[:project_id]}/logs/#{params[:log_name]}"
240
+ @logs_sent << JSON.parse(request.body).merge('logName' => log_name)
241
+ { body: '' }
242
+ end
243
+ end
244
+ yield
245
+ end
246
+
247
+ # Create a Fluentd output test driver with the Google Cloud Output plugin.
248
+ def create_driver(conf = APPLICATION_DEFAULT_CONFIG, tag = 'test')
249
+ Fluent::Test::BufferedOutputTestDriver.new(
250
+ Fluent::GoogleCloudOutput, tag).configure(conf, true)
251
+ end
252
+
253
+ # Verify the number and the content of the log entries match the expectation.
254
+ # The caller can optionally provide a block which is called for each entry.
255
+ def verify_log_entries(n, params, payload_type = 'textPayload', &block)
256
+ verify_json_log_entries(n, params, payload_type, &block)
257
+ end
258
+
259
+ # For an optional field with default values, Protobuf omits the field when it
260
+ # is deserialized to json. So we need to add an extra check for gRPC which
261
+ # uses Protobuf.
262
+ #
263
+ # An optional block can be passed in if we need to assert something other than
264
+ # a plain equal. e.g. assert_in_delta.
265
+ def assert_equal_with_default(field, expected_value, _default_value, entry)
266
+ if block_given?
267
+ yield
268
+ else
269
+ assert_equal expected_value, field, entry
270
+ end
271
+ end
272
+
273
+ # Get the fields of the struct payload.
274
+ def get_fields(struct_payload)
275
+ struct_payload
276
+ end
277
+
278
+ # Get the value of a struct field.
279
+ def get_struct(field)
280
+ field
281
+ end
282
+
283
+ # Get the value of a string field.
284
+ def get_string(field)
285
+ field
286
+ end
287
+
288
+ # Get the value of a number field.
289
+ def get_number(field)
290
+ field
291
+ end
292
+
293
+ # The null value.
294
+ def null_value
295
+ nil
296
+ end
297
+ end