fluent-plugin-bigquery 0.2.11 → 0.2.12
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.
- checksums.yaml +4 -4
- data/README.md +63 -8
- data/fluent-plugin-bigquery.gemspec +1 -0
- data/lib/fluent/plugin/bigquery/version.rb +1 -1
- data/lib/fluent/plugin/out_bigquery.rb +43 -18
- data/test/plugin/test_out_bigquery.rb +100 -11
- data/test/plugin/testdata/json_key.json +7 -0
- metadata +18 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 24d39b32bcb8f6028618041edda11e1ffd3f6d16
|
4
|
+
data.tar.gz: a238724b34f64d36f7f319b0d6fc43f578b0dffc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46e8ffbe6007cd2d855114671121285b4fb89b9fab378b59fcc37a5c20bd9f6587a5f8ab96a4f684cf9464d6d9da5b42b7a94f9d74e2cd6fd6769cf9c5f2f5df
|
7
|
+
data.tar.gz: 1abae1988128f8b2349aa898f1afb5f847e894c2ee88bd34126cb5981ffa0af5dd3fcf74c2e1f27fdc3c1d45814b2b000b932100f94e795ed27e8ab90e9b7894
|
data/README.md
CHANGED
@@ -110,15 +110,57 @@ section in the Google BigQuery document.
|
|
110
110
|
|
111
111
|
There are two methods supported to fetch access token for the service account.
|
112
112
|
|
113
|
-
1. Public-Private key pair
|
114
|
-
2.
|
113
|
+
1. Public-Private key pair of GCP(Google Cloud Platform)'s service account
|
114
|
+
2. JSON key of GCP(Google Cloud Platform)'s service account
|
115
|
+
3. Predefined access token (Compute Engine only)
|
116
|
+
4. Google application default credentials (http://goo.gl/IUuyuX)
|
115
117
|
|
116
|
-
|
118
|
+
#### Public-Private key pair of GCP's service account
|
119
|
+
|
120
|
+
The examples above use the first one. You first need to create a service account (client ID),
|
117
121
|
download its private key and deploy the key with fluentd.
|
118
122
|
|
119
|
-
|
120
|
-
|
121
|
-
|
123
|
+
#### JSON key of GCP(Google Cloud Platform)'s service account
|
124
|
+
|
125
|
+
You first need to create a service account (client ID),
|
126
|
+
download its JSON key and deploy the key with fluentd.
|
127
|
+
|
128
|
+
```apache
|
129
|
+
<match dummy>
|
130
|
+
type bigquery
|
131
|
+
|
132
|
+
auth_method json_key
|
133
|
+
json_key /home/username/.keys/00000000000000000000000000000000-jsonkey.json
|
134
|
+
|
135
|
+
project yourproject_id
|
136
|
+
dataset yourdataset_id
|
137
|
+
table tablename
|
138
|
+
...
|
139
|
+
</match>
|
140
|
+
```
|
141
|
+
|
142
|
+
You can also provide `json_key` as embedded JSON string like this.
|
143
|
+
You need to only include `private_key` and `client_email` key from JSON key file.
|
144
|
+
|
145
|
+
```apache
|
146
|
+
<match dummy>
|
147
|
+
type bigquery
|
148
|
+
|
149
|
+
auth_method json_key
|
150
|
+
json_key {"private_key": "-----BEGIN PRIVATE KEY-----\n...", "client_email": "xxx@developer.gserviceaccount.com"}
|
151
|
+
|
152
|
+
project yourproject_id
|
153
|
+
dataset yourdataset_id
|
154
|
+
table tablename
|
155
|
+
...
|
156
|
+
</match>
|
157
|
+
```
|
158
|
+
|
159
|
+
#### Predefined access token (Compute Engine only)
|
160
|
+
|
161
|
+
When you run fluentd on Googlce Compute Engine instance,
|
162
|
+
you don't need to explicitly create a service account for fluentd.
|
163
|
+
In this authentication method, you need to add the API scope "https://www.googleapis.com/auth/bigquery" to the scope list of your
|
122
164
|
Compute Engine instance, then you can configure fluentd like this.
|
123
165
|
|
124
166
|
```apache
|
@@ -141,6 +183,19 @@ Compute Engine instance, then you can configure fluentd like this.
|
|
141
183
|
</match>
|
142
184
|
```
|
143
185
|
|
186
|
+
#### Application default credentials
|
187
|
+
|
188
|
+
The Application Default Credentials provide a simple way to get authorization credentials for use in calling Google APIs, which are described in detail at http://goo.gl/IUuyuX.
|
189
|
+
|
190
|
+
In this authentication method, the credentials returned are determined by the environment the code is running in. Conditions are checked in the following order:credentials are get from following order.
|
191
|
+
|
192
|
+
1. The environment variable `GOOGLE_APPLICATION_CREDENTIALS` is checked. If this variable is specified it should point to a JSON key file that defines the credentials.
|
193
|
+
2. The environment variable `GOOGLE_PRIVATE_KEY` and `GOOGLE_CLIENT_EMAIL` are checked. If this variables are specified `GOOGLE_PRIVATE_KEY` should point to `private_key`, `GOOGLE_CLIENT_EMAIL` should point to `client_email` in a JSON key.
|
194
|
+
3. Well known path is checked. If file is exists, the file used as a JSON key file. This path is `$HOME/.config/gcloud/application_default_credentials.json`.
|
195
|
+
4. System default path is checked. If file is exists, the file used as a JSON key file. This path is `/etc/google/auth/application_default_credentials.json`.
|
196
|
+
5. If you are running in Google Compute Engine production, the built-in service account associated with the virtual machine instance will be used.
|
197
|
+
6. If none of these conditions is true, an error will occur.
|
198
|
+
|
144
199
|
### Table id formatting
|
145
200
|
|
146
201
|
`table` and `tables` options accept [Time#strftime](http://ruby-doc.org/core-1.9.3/Time.html#method-i-strftime)
|
@@ -282,9 +337,9 @@ You can set `insert_id_field` option to specify the field to use as `insertId` p
|
|
282
337
|
```apache
|
283
338
|
<match dummy>
|
284
339
|
type bigquery
|
285
|
-
|
340
|
+
|
286
341
|
...
|
287
|
-
|
342
|
+
|
288
343
|
insert_id_field uuid
|
289
344
|
field_string uuid
|
290
345
|
</match>
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_development_dependency "test-unit-rr", "~> 1.0.3"
|
25
25
|
|
26
26
|
spec.add_runtime_dependency "google-api-client", "~> 0.8.0"
|
27
|
+
spec.add_runtime_dependency "googleauth"
|
27
28
|
spec.add_runtime_dependency "fluentd"
|
28
29
|
spec.add_runtime_dependency "fluent-mixin-plaintextformatter", '>= 0.2.1'
|
29
30
|
spec.add_runtime_dependency "fluent-mixin-config-placeholders", ">= 0.3.0"
|
@@ -38,14 +38,17 @@ module Fluent
|
|
38
38
|
# config_param :client_secret, :string
|
39
39
|
|
40
40
|
# Available methods are:
|
41
|
-
# * private_key -- Use service account credential
|
41
|
+
# * private_key -- Use service account credential from pkcs12 private key file
|
42
42
|
# * compute_engine -- Use access token available in instances of ComputeEngine
|
43
|
+
# * private_json_key -- Use service account credential from JSON key
|
44
|
+
# * application_default -- Use application default credential
|
43
45
|
config_param :auth_method, :string, default: 'private_key'
|
44
46
|
|
45
47
|
### Service Account credential
|
46
48
|
config_param :email, :string, default: nil
|
47
49
|
config_param :private_key_path, :string, default: nil
|
48
50
|
config_param :private_key_passphrase, :string, default: 'notasecret', secret: true
|
51
|
+
config_param :json_key, default: nil
|
49
52
|
|
50
53
|
# see as simple reference
|
51
54
|
# https://github.com/abronte/BigQuery/blob/master/lib/bigquery.rb
|
@@ -128,9 +131,10 @@ module Fluent
|
|
128
131
|
super
|
129
132
|
require 'json'
|
130
133
|
require 'google/api_client'
|
131
|
-
require '
|
132
|
-
|
133
|
-
|
134
|
+
require 'googleauth'
|
135
|
+
|
136
|
+
# MEMO: signet-0.6.1 depend on Farady.default_connection
|
137
|
+
Faraday.default_connection.options.timeout = 60
|
134
138
|
end
|
135
139
|
|
136
140
|
# Define `log` method for v0.10.42 or earlier
|
@@ -148,6 +152,12 @@ module Fluent
|
|
148
152
|
end
|
149
153
|
when 'compute_engine'
|
150
154
|
# Do nothing
|
155
|
+
when 'json_key'
|
156
|
+
unless @json_key
|
157
|
+
raise Fluent::ConfigError, "'json_key' must be specified if auth_method == 'json_key'"
|
158
|
+
end
|
159
|
+
when 'application_default'
|
160
|
+
# Do nothing
|
151
161
|
else
|
152
162
|
raise Fluent::ConfigError, "unrecognized 'auth_method': #{@auth_method}"
|
153
163
|
end
|
@@ -227,26 +237,41 @@ module Fluent
|
|
227
237
|
application_version: Fluent::BigQueryPlugin::VERSION
|
228
238
|
)
|
229
239
|
|
240
|
+
scope = "https://www.googleapis.com/auth/bigquery"
|
241
|
+
|
230
242
|
case @auth_method
|
231
243
|
when 'private_key'
|
232
|
-
key = Google::APIClient::
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
client.authorization = asserter.authorize
|
244
|
+
key = Google::APIClient::KeyUtils.load_from_pkcs12(@private_key_path, @private_key_passphrase)
|
245
|
+
auth = Signet::OAuth2::Client.new(
|
246
|
+
token_credential_uri: "https://accounts.google.com/o/oauth2/token",
|
247
|
+
audience: "https://accounts.google.com/o/oauth2/token",
|
248
|
+
scope: scope,
|
249
|
+
issuer: @email,
|
250
|
+
signing_key: key)
|
240
251
|
|
241
252
|
when 'compute_engine'
|
242
|
-
auth = Google::
|
243
|
-
|
244
|
-
|
253
|
+
auth = Google::Auth::GCECredentials.new
|
254
|
+
|
255
|
+
when 'json_key'
|
256
|
+
if File.exist?(@json_key)
|
257
|
+
auth = File.open(@json_key) do |f|
|
258
|
+
Google::Auth::ServiceAccountCredentials.new(json_key_io: f, scope: scope)
|
259
|
+
end
|
260
|
+
else
|
261
|
+
key = StringIO.new(@json_key)
|
262
|
+
auth = Google::Auth::ServiceAccountCredentials.new(json_key_io: key, scope: scope)
|
263
|
+
end
|
264
|
+
|
265
|
+
when 'application_default'
|
266
|
+
auth = Google::Auth.get_application_default([scope])
|
245
267
|
|
246
268
|
else
|
247
269
|
raise ConfigError, "Unknown auth method: #{@auth_method}"
|
248
270
|
end
|
249
271
|
|
272
|
+
auth.fetch_access_token!
|
273
|
+
client.authorization = auth
|
274
|
+
|
250
275
|
@cached_client_expiration = Time.now + 1800
|
251
276
|
@cached_client = client
|
252
277
|
end
|
@@ -349,7 +374,7 @@ module Fluent
|
|
349
374
|
if @replace_record_key
|
350
375
|
record = replace_record_key(record)
|
351
376
|
end
|
352
|
-
|
377
|
+
|
353
378
|
row = @fields.format(@add_time_field.call(record, time))
|
354
379
|
unless row.empty?
|
355
380
|
row = {"json" => row}
|
@@ -442,7 +467,7 @@ module Fluent
|
|
442
467
|
### https://developers.google.com/bigquery/docs/tables
|
443
468
|
# Each field has the following properties:
|
444
469
|
#
|
445
|
-
# name - The name must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_),
|
470
|
+
# name - The name must contain only letters (a-z, A-Z), numbers (0-9), or underscores (_),
|
446
471
|
# and must start with a letter or underscore. The maximum length is 128 characters.
|
447
472
|
# https://cloud.google.com/bigquery/docs/reference/v2/tables#schema.fields.name
|
448
473
|
unless name =~ /^[_A-Za-z][_A-Za-z0-9]{,127}$/
|
@@ -468,7 +493,7 @@ module Fluent
|
|
468
493
|
end
|
469
494
|
|
470
495
|
def format_one(value)
|
471
|
-
raise NotImplementedError, "Must implement in a subclass"
|
496
|
+
raise NotImplementedError, "Must implement in a subclass"
|
472
497
|
end
|
473
498
|
|
474
499
|
def to_h
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
require 'google/api_client'
|
3
|
+
require 'googleauth'
|
3
4
|
require 'fluent/plugin/buf_memory'
|
4
5
|
|
5
6
|
class BigQueryOutputTest < Test::Unit::TestCase
|
@@ -60,14 +61,20 @@ class BigQueryOutputTest < Test::Unit::TestCase
|
|
60
61
|
}
|
61
62
|
end
|
62
63
|
|
63
|
-
def
|
64
|
+
def test_configure_auth_private_key
|
64
65
|
key = stub!
|
65
|
-
mock(Google::APIClient::
|
66
|
+
mock(Google::APIClient::KeyUtils).load_from_pkcs12('/path/to/key', 'notasecret') { key }
|
66
67
|
authorization = Object.new
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
68
|
+
mock(authorization).fetch_access_token!
|
69
|
+
stub(Signet::OAuth2::Client).new
|
70
|
+
mock(Signet::OAuth2::Client).new(
|
71
|
+
token_credential_uri: "https://accounts.google.com/o/oauth2/token",
|
72
|
+
audience: "https://accounts.google.com/o/oauth2/token",
|
73
|
+
scope: API_SCOPE,
|
74
|
+
issuer: 'foo@bar.example',
|
75
|
+
signing_key: key) { authorization }
|
76
|
+
|
77
|
+
mock.proxy(Google::APIClient).new.with_any_args {
|
71
78
|
mock!.__send__(:authorization=, authorization) {}
|
72
79
|
}
|
73
80
|
|
@@ -75,6 +82,88 @@ class BigQueryOutputTest < Test::Unit::TestCase
|
|
75
82
|
driver.instance.client()
|
76
83
|
end
|
77
84
|
|
85
|
+
def test_configure_auth_compute_engine
|
86
|
+
authorization = Object.new
|
87
|
+
mock(authorization).fetch_access_token!
|
88
|
+
mock(Google::Auth::GCECredentials).new { authorization }
|
89
|
+
|
90
|
+
mock.proxy(Google::APIClient).new.with_any_args {
|
91
|
+
mock!.__send__(:authorization=, authorization) {}
|
92
|
+
}
|
93
|
+
|
94
|
+
driver = create_driver(%[
|
95
|
+
table foo
|
96
|
+
auth_method compute_engine
|
97
|
+
project yourproject_id
|
98
|
+
dataset yourdataset_id
|
99
|
+
field_integer time,status,bytes
|
100
|
+
])
|
101
|
+
driver.instance.client()
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_configure_auth_json_key_as_file
|
105
|
+
json_key_path = 'test/plugin/testdata/json_key.json'
|
106
|
+
authorization = Object.new
|
107
|
+
mock(authorization).fetch_access_token!
|
108
|
+
mock(Google::Auth::ServiceAccountCredentials).new(json_key_io: File.open(json_key_path), scope: API_SCOPE) { authorization }
|
109
|
+
|
110
|
+
mock.proxy(Google::APIClient).new.with_any_args {
|
111
|
+
mock!.__send__(:authorization=, authorization) {}
|
112
|
+
}
|
113
|
+
|
114
|
+
driver = create_driver(%[
|
115
|
+
table foo
|
116
|
+
auth_method json_key
|
117
|
+
json_key #{json_key_path}
|
118
|
+
project yourproject_id
|
119
|
+
dataset yourdataset_id
|
120
|
+
field_integer time,status,bytes
|
121
|
+
])
|
122
|
+
driver.instance.client()
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_configure_auth_json_key_as_string
|
126
|
+
json_key = '{"private_key": "X", "client_email": "xxx@developer.gserviceaccount.com"}'
|
127
|
+
json_key_io = StringIO.new(json_key)
|
128
|
+
mock(StringIO).new(json_key) { json_key_io }
|
129
|
+
authorization = Object.new
|
130
|
+
mock(authorization).fetch_access_token!
|
131
|
+
mock(Google::Auth::ServiceAccountCredentials).new(json_key_io: json_key_io, scope: API_SCOPE) { authorization }
|
132
|
+
|
133
|
+
mock.proxy(Google::APIClient).new.with_any_args {
|
134
|
+
mock!.__send__(:authorization=, authorization) {}
|
135
|
+
}
|
136
|
+
|
137
|
+
driver = create_driver(%[
|
138
|
+
table foo
|
139
|
+
auth_method json_key
|
140
|
+
json_key #{json_key}
|
141
|
+
project yourproject_id
|
142
|
+
dataset yourdataset_id
|
143
|
+
field_integer time,status,bytes
|
144
|
+
])
|
145
|
+
driver.instance.client()
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_configure_auth_application_default
|
149
|
+
authorization = Object.new
|
150
|
+
mock(authorization).fetch_access_token!
|
151
|
+
mock(Google::Auth).get_application_default([API_SCOPE]) { authorization }
|
152
|
+
|
153
|
+
mock.proxy(Google::APIClient).new.with_any_args {
|
154
|
+
mock!.__send__(:authorization=, authorization) {}
|
155
|
+
}
|
156
|
+
|
157
|
+
driver = create_driver(%[
|
158
|
+
table foo
|
159
|
+
auth_method application_default
|
160
|
+
project yourproject_id
|
161
|
+
dataset yourdataset_id
|
162
|
+
field_integer time,status,bytes
|
163
|
+
])
|
164
|
+
driver.instance.client()
|
165
|
+
end
|
166
|
+
|
78
167
|
def test_configure_fieldname_stripped
|
79
168
|
driver = create_driver(%[
|
80
169
|
table foo
|
@@ -86,10 +175,10 @@ class BigQueryOutputTest < Test::Unit::TestCase
|
|
86
175
|
time_format %s
|
87
176
|
time_field time
|
88
177
|
|
89
|
-
field_integer time , status , bytes
|
178
|
+
field_integer time , status , bytes
|
90
179
|
field_string _log_name, vhost, path, method, protocol, agent, referer, remote.host, remote.ip, remote.user
|
91
|
-
field_float requesttime
|
92
|
-
field_boolean bot_access , loginsession
|
180
|
+
field_float requesttime
|
181
|
+
field_boolean bot_access , loginsession
|
93
182
|
])
|
94
183
|
fields = driver.instance.instance_eval{ @fields }
|
95
184
|
|
@@ -483,7 +572,7 @@ class BigQueryOutputTest < Test::Unit::TestCase
|
|
483
572
|
assert fields["tty"]
|
484
573
|
assert_equal :string, fields["tty"].type
|
485
574
|
assert_equal :nullable, fields["tty"].mode
|
486
|
-
|
575
|
+
|
487
576
|
assert fields["pwd"]
|
488
577
|
assert_equal :string, fields["pwd"].type
|
489
578
|
assert_equal :required, fields["pwd"].mode
|
@@ -560,7 +649,7 @@ class BigQueryOutputTest < Test::Unit::TestCase
|
|
560
649
|
assert fields["tty"]
|
561
650
|
assert_equal :string, fields["tty"].type
|
562
651
|
assert_equal :nullable, fields["tty"].mode
|
563
|
-
|
652
|
+
|
564
653
|
assert fields["pwd"]
|
565
654
|
assert_equal :string, fields["pwd"].type
|
566
655
|
assert_equal :required, fields["pwd"].mode
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-bigquery
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Naoya Ito
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-10-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: 0.8.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: googleauth
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: fluentd
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,6 +185,7 @@ files:
|
|
171
185
|
- test/helper.rb
|
172
186
|
- test/plugin/test_out_bigquery.rb
|
173
187
|
- test/plugin/testdata/apache.schema
|
188
|
+
- test/plugin/testdata/json_key.json
|
174
189
|
- test/plugin/testdata/sudo.schema
|
175
190
|
- test/test_load_request_body_wrapper.rb
|
176
191
|
homepage: https://github.com/kaizenplatform/fluent-plugin-bigquery
|
@@ -201,5 +216,6 @@ test_files:
|
|
201
216
|
- test/helper.rb
|
202
217
|
- test/plugin/test_out_bigquery.rb
|
203
218
|
- test/plugin/testdata/apache.schema
|
219
|
+
- test/plugin/testdata/json_key.json
|
204
220
|
- test/plugin/testdata/sudo.schema
|
205
221
|
- test/test_load_request_body_wrapper.rb
|