fluent-plugin-bigquery 0.2.11 → 0.2.12

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
  SHA1:
3
- metadata.gz: e93c58d0a0c1da28a59026a81f9d060ec3b0e532
4
- data.tar.gz: ff810e3c6177a362a28a14cd92ea3091294212d4
3
+ metadata.gz: 24d39b32bcb8f6028618041edda11e1ffd3f6d16
4
+ data.tar.gz: a238724b34f64d36f7f319b0d6fc43f578b0dffc
5
5
  SHA512:
6
- metadata.gz: 86e853fdad235e8ab2b014e100b4e1f355ff9e11da27605e30d66aa9676de3af888f8ab0499445b42df60c884114d5d380c4fa79f6b92ee226063554c4f1344c
7
- data.tar.gz: a8e3d37c866073bfd54d25d15482387bb3454191ab5f6a41718c4a7b324d838e275b3dc2a8ce33eb213f57ee0d9a21b66cd878f5b3aec7f555be179238c4dedd
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. Predefined access token (Compute Engine only)
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
- The examples above use the first one. You first need to create a service account (client ID),
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
- On the other hand, you don't need to explicitly create a service account for fluentd when you
120
- run fluentd in Google Compute Engine. In this second authentication method, you need to
121
- add the API scope "https://www.googleapis.com/auth/bigquery" to the scope list of your
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"
@@ -1,6 +1,6 @@
1
1
  module Fluent
2
2
  module BigQueryPlugin
3
- VERSION = "0.2.11"
3
+ VERSION = "0.2.12"
4
4
  end
5
5
  end
6
6
 
@@ -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 'google/api_client/client_secrets'
132
- require 'google/api_client/auth/installed_app'
133
- require 'google/api_client/auth/compute_service_account'
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::PKCS12.load_key( @private_key_path, @private_key_passphrase )
233
- asserter = Google::APIClient::JWTAsserter.new(
234
- @email,
235
- "https://www.googleapis.com/auth/bigquery",
236
- key
237
- )
238
- # refresh_auth
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::APIClient::ComputeServiceAccount.new
243
- auth.fetch_access_token!
244
- client.authorization = auth
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 test_configure_auth
64
+ def test_configure_auth_private_key
64
65
  key = stub!
65
- mock(Google::APIClient::PKCS12).load_key('/path/to/key', 'notasecret') { key }
66
+ mock(Google::APIClient::KeyUtils).load_from_pkcs12('/path/to/key', 'notasecret') { key }
66
67
  authorization = Object.new
67
- asserter = mock!.authorize { authorization }
68
- mock(Google::APIClient::JWTAsserter).new('foo@bar.example', API_SCOPE, key) { asserter }
69
-
70
- mock.proxy(Google::APIClient).new.with_any_args {
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
@@ -0,0 +1,7 @@
1
+ {
2
+ "private_key_id": "1",
3
+ "private_key": "X",
4
+ "client_email": "xxx@developer.gserviceaccount.com",
5
+ "client_id": "xxx.apps.googleusercontent.com",
6
+ "type": "service_account"
7
+ }
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.11
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-08-24 00:00:00.000000000 Z
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