fluent-plugin-out-kivera 1.0.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.
- checksums.yaml +7 -0
- data/.github/workflows/linux.yml +26 -0
- data/.github/workflows/macos.yml +26 -0
- data/.gitignore +8 -0
- data/CHANGELOG.md +80 -0
- data/Gemfile +4 -0
- data/ISSUE_TEMPLATE.md +21 -0
- data/LICENSE.txt +11 -0
- data/README.md +58 -0
- data/Rakefile +11 -0
- data/fluent-plugin-out-kivera.gemspec +26 -0
- data/lib/fluent/plugin/out_kivera.rb +378 -0
- data/lib/fluent/test/http_output_test.rb +42 -0
- data/test/plugin/script/plugin/formatter_test.rb +21 -0
- data/test/plugin/test_out_kivera.rb +872 -0
- metadata +150 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce321115bd4b6032242005171702e374148359e62998ca3a4175ab6ae4aa8add
|
4
|
+
data.tar.gz: 5891e23d0f951706f17314e708fed643761901f60eafe6e046abddc5e9bed907
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 63dc2922e9e0771369c125b3e33105656a9237703e8b5cd0cded8f7808481707af395f3416e8abedb26ffec891a3fee262b6290a841112120d6341c1cd671afa
|
7
|
+
data.tar.gz: f47e150d7a7d920d5b4ada2e5d53a758efd39c4b3da4a896fedf7f42cc6027340837e9c77c350fd216fecf80145e5dcf5ec1e01f9fa443c8ebb4e72c12246ad5
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Testing on Ubuntu
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ${{ matrix.os }}
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: [ '2.4', '2.5', '2.6', '2.7' ]
|
12
|
+
os:
|
13
|
+
- ubuntu-latest
|
14
|
+
name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
- name: unit testing
|
21
|
+
env:
|
22
|
+
CI: true
|
23
|
+
run: |
|
24
|
+
gem install bundler rake
|
25
|
+
bundle install --jobs 4 --retry 3
|
26
|
+
bundle exec rake test
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Testing on macOS
|
2
|
+
on:
|
3
|
+
- push
|
4
|
+
- pull_request
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ${{ matrix.os }}
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
ruby: [ '2.4', '2.5', '2.6', '2.7' ]
|
12
|
+
os:
|
13
|
+
- macOS-latest
|
14
|
+
name: Ruby ${{ matrix.ruby }} unit testing on ${{ matrix.os }}
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- uses: ruby/setup-ruby@v1
|
18
|
+
with:
|
19
|
+
ruby-version: ${{ matrix.ruby }}
|
20
|
+
- name: unit testing
|
21
|
+
env:
|
22
|
+
CI: true
|
23
|
+
run: |
|
24
|
+
gem install bundler rake
|
25
|
+
bundle install --jobs 4 --retry 3
|
26
|
+
bundle exec rake test
|
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# Changelog
|
2
|
+
## 1.3.3
|
3
|
+
* Revert x-ndjson format payload behavior
|
4
|
+
|
5
|
+
## 1.3.2
|
6
|
+
* Fix invalid x-ndjson payload format
|
7
|
+
|
8
|
+
## 1.3.1
|
9
|
+
* Support compression request
|
10
|
+
|
11
|
+
## 1.3.0
|
12
|
+
* Support all private key types
|
13
|
+
* Recoverable error codes
|
14
|
+
* Bulk request with x-ndjson
|
15
|
+
|
16
|
+
## 1.2.0
|
17
|
+
* Support mutual authentication
|
18
|
+
|
19
|
+
## 1.1.7
|
20
|
+
* Fix dependent Fluentd version
|
21
|
+
|
22
|
+
## 1.1.6
|
23
|
+
* Pass chunk directly info built-in placeholder instead of chunk.metadata
|
24
|
+
|
25
|
+
## 1.1.5
|
26
|
+
* Add :raw serializer
|
27
|
+
|
28
|
+
## 1.1.4
|
29
|
+
* Add custom formatter feature
|
30
|
+
* Tweak Travis CI tasks
|
31
|
+
|
32
|
+
## 1.1.3
|
33
|
+
* Send query_string to endpoint_url
|
34
|
+
|
35
|
+
## 1.1.2
|
36
|
+
* Added custom headers feature
|
37
|
+
|
38
|
+
## 1.1.1
|
39
|
+
* Added plain text transport capability
|
40
|
+
* Added specify cacert file for ssl verify
|
41
|
+
|
42
|
+
## 1.1.0
|
43
|
+
* Support for jwt token authentication
|
44
|
+
|
45
|
+
## 1.0.1
|
46
|
+
* Added endpoint_url placeholder support
|
47
|
+
|
48
|
+
## 1.0.0
|
49
|
+
* Use Fluentd v1 API
|
50
|
+
|
51
|
+
## 0.2.0
|
52
|
+
### Added
|
53
|
+
* SSL is now supported if `endpoint_url` uses the `https` scheme (uses ruby-2.1 syntax internally)
|
54
|
+
* New config: set `ssl_no_verify` to `true` to bypass SSL certificate verification.
|
55
|
+
Use at your own risk.
|
56
|
+
### Changed
|
57
|
+
* Fixed tests:
|
58
|
+
* Removed some warnings
|
59
|
+
* Fixed failing binary test to use UTF-8
|
60
|
+
### Removed
|
61
|
+
* Dropped support of Ruby 1.9-2.0
|
62
|
+
|
63
|
+
## 0.1.4
|
64
|
+
* #11 Updated Fluentd dependency to: [">= 0.10.0", "< 2"]
|
65
|
+
* #10 `password` is now marked as a [secret option](https://github.com/fluent/fluentd/pull/604)
|
66
|
+
|
67
|
+
## 0.1.3
|
68
|
+
* Added a new configuration option: `raise_on_error` (default: true)
|
69
|
+
* In order to let the plugin raise exceptions like it did in 0.1.1: keep using your configuration as-is
|
70
|
+
* In order to suppress all exceptions: add `raise_on_error false` to your configuration
|
71
|
+
|
72
|
+
## 0.1.2
|
73
|
+
* #6 Catch all `StandardError`s during HTTP request to prevent td-agent from freezing
|
74
|
+
|
75
|
+
## 0.1.1
|
76
|
+
* #2 Use yajl instead of json as json serializer
|
77
|
+
* #1 Fix a bug where a nil HTTP response caused the plugin to stop working
|
78
|
+
|
79
|
+
## 0.1.0
|
80
|
+
* Initial release
|
data/Gemfile
ADDED
data/ISSUE_TEMPLATE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#### Problem
|
2
|
+
|
3
|
+
...
|
4
|
+
|
5
|
+
#### Steps to replicate
|
6
|
+
|
7
|
+
Provide example config and message
|
8
|
+
|
9
|
+
#### Expected Behavior or What you need to ask
|
10
|
+
|
11
|
+
...
|
12
|
+
|
13
|
+
#### Using Fluentd and out_kivera plugin versions
|
14
|
+
|
15
|
+
* OS version
|
16
|
+
* Fluentd v0.12 or v0.14/v1.0
|
17
|
+
* paste result of ``fluentd --version`` or ``td-agent --version``
|
18
|
+
* out_kivera plugin 1.x.y or 0.x.y
|
19
|
+
* paste boot log of fluentd or td-agent
|
20
|
+
* paste result of ``fluent-gem list``, ``td-agent-gem list`` or your Gemfile.lock
|
21
|
+
* Bear Metal or Within Docker or Kubernetes or others? (optional)
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
you may not use this file except in compliance with the License.
|
3
|
+
You may obtain a copy of the License at
|
4
|
+
|
5
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
See the License for the specific language governing permissions and
|
11
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# fluent-plugin-out-kivera, a plugin for [Fluentd](http://fluentd.org)
|
2
|
+
|
3
|
+
A generic [fluentd][1] output plugin for sending Kivera proxy logs to the Kivera log ingestion service.
|
4
|
+
|
5
|
+
## Configuration options
|
6
|
+
|
7
|
+
You can specify the path to a config.json file which can contain the following parameters:
|
8
|
+
|
9
|
+
*config.json*
|
10
|
+
{
|
11
|
+
"client_id": "",
|
12
|
+
"client_secret": "",
|
13
|
+
"audience": "",
|
14
|
+
"auth0_domain": "",
|
15
|
+
"auth0_cert": "",
|
16
|
+
"auth0_cert_file": ""
|
17
|
+
}
|
18
|
+
|
19
|
+
Note that parameter specified within the config_file will take precendence over parameters specified in your fluent.conf.
|
20
|
+
Also note that the auth0_cert parameter will take precedence over the auth0_cert_file parameter.
|
21
|
+
|
22
|
+
*fluent.conf*
|
23
|
+
<match *>
|
24
|
+
@type kivera
|
25
|
+
endpoint_url http://localhost.local/api/
|
26
|
+
config_file /path/to/config_file.json
|
27
|
+
client_id abc123
|
28
|
+
client_secret def456
|
29
|
+
audience http://api.kivera.io
|
30
|
+
auth0_domain auth.nonp.kivera.io
|
31
|
+
auth0_cert -----BEGIN CERTIFICATE-----...
|
32
|
+
auth0_cert_file /path/to/auth0_cert_file
|
33
|
+
ssl_no_verify false # default: false
|
34
|
+
rate_limit_msec 100 # default: 0 = no rate limiting
|
35
|
+
raise_on_error false # default: true
|
36
|
+
recoverable_status_codes 503, 400 # default: 503
|
37
|
+
custom_headers {"token":"arbitrary"} # default: nil
|
38
|
+
buffered true # default: false. Switch non-buffered/buffered mode
|
39
|
+
bulk_request true # default: true. Send events as application/x-ndjson
|
40
|
+
compress_request true # default: false. Send compressed events
|
41
|
+
</match>
|
42
|
+
|
43
|
+
## Usage notes
|
44
|
+
|
45
|
+
If you'd like to retry failed requests, consider using [fluent-plugin-bufferize][3].
|
46
|
+
Or, specify appropriate `recoverable_status_codes` parameter.
|
47
|
+
|
48
|
+
To send events with bulk_request, you should specify `bulk_request` as `true`
|
49
|
+
Note that when this parameter as `true`, Fluentd always send events as `application/x-ndjson`.
|
50
|
+
Currently, `application/x-ndjson` is only supported MIME type for bulk_request.
|
51
|
+
|
52
|
+
----
|
53
|
+
|
54
|
+
Heavily based on [fluent-plugin-out-http][2]
|
55
|
+
|
56
|
+
[1]: http://fluentd.org/
|
57
|
+
[2]: https://github.com/fluent-plugins-nursery/fluent-plugin-out-http
|
58
|
+
[3]: https://github.com/sabottenda/fluent-plugin-bufferize
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.name = "fluent-plugin-out-kivera"
|
5
|
+
gem.version = "1.0.1"
|
6
|
+
gem.authors = ["Tyler Matheson"]
|
7
|
+
gem.email = ["support@kivera.io"]
|
8
|
+
gem.summary = "Fluentd plugin for Kivera"
|
9
|
+
gem.description = "A Fluentd output plugin for sending Kivera proxy logs to the Kivera log ingestion service"
|
10
|
+
gem.homepage = "https://github.com/kivera-io/fluent-plugin-out-kivera"
|
11
|
+
gem.licenses = ["Apache-2.0"]
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.require_paths = ["lib"]
|
17
|
+
|
18
|
+
gem.required_ruby_version = '>= 2.1.0'
|
19
|
+
|
20
|
+
gem.add_runtime_dependency "yajl-ruby", "~> 1.0"
|
21
|
+
gem.add_runtime_dependency "fluentd", [">= 0.14.22", "< 2"]
|
22
|
+
gem.add_runtime_dependency "jwt", '~> 2.2'
|
23
|
+
gem.add_development_dependency "bundler"
|
24
|
+
gem.add_development_dependency "rake"
|
25
|
+
gem.add_development_dependency "test-unit", ">= 3.1.0"
|
26
|
+
end
|
@@ -0,0 +1,378 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
require 'yajl'
|
4
|
+
require 'fluent/plugin/output'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'openssl'
|
7
|
+
require 'zlib'
|
8
|
+
require 'jwt'
|
9
|
+
|
10
|
+
class Fluent::Plugin::HTTPOutput < Fluent::Plugin::Output
|
11
|
+
Fluent::Plugin.register_output('kivera', self)
|
12
|
+
|
13
|
+
class RecoverableResponse < StandardError; end
|
14
|
+
|
15
|
+
helpers :compat_parameters, :formatter, :storage
|
16
|
+
|
17
|
+
DEFAULT_STORAGE_TYPE = "local"
|
18
|
+
DEFAULT_BUFFER_TYPE = "memory"
|
19
|
+
DEFAULT_FORMATTER = "json"
|
20
|
+
TOKEN_EXPIRY_OFFSET = 300
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
super
|
24
|
+
end
|
25
|
+
|
26
|
+
# Endpoint URL ex. http://localhost.local/api/
|
27
|
+
config_param :endpoint_url, :string
|
28
|
+
|
29
|
+
# Set Net::HTTP.verify_mode to `OpenSSL::SSL::VERIFY_NONE`
|
30
|
+
config_param :ssl_no_verify, :bool, :default => false
|
31
|
+
|
32
|
+
# Simple rate limiting: ignore any records within `rate_limit_msec`
|
33
|
+
# since the last one.
|
34
|
+
config_param :rate_limit_msec, :integer, :default => 0
|
35
|
+
|
36
|
+
# Raise errors that were rescued during HTTP requests?
|
37
|
+
config_param :raise_on_error, :bool, :default => true
|
38
|
+
|
39
|
+
# Specify recoverable error codes
|
40
|
+
config_param :recoverable_status_codes, :array, value_type: :integer, default: [503]
|
41
|
+
|
42
|
+
# kivera proxy client config file
|
43
|
+
config_param :config_file, :string, default: ""
|
44
|
+
|
45
|
+
# kivera proxy client id
|
46
|
+
config_param :client_id, :string, default: ""
|
47
|
+
|
48
|
+
# kivera proxy client secret
|
49
|
+
config_param :client_secret, :string, default: ""
|
50
|
+
|
51
|
+
# kivera audience api
|
52
|
+
config_param :audience, :string, default: ""
|
53
|
+
|
54
|
+
# explicit Kivera auth0 certificate as a string
|
55
|
+
config_param :auth0_cert, :string, default: ""
|
56
|
+
|
57
|
+
# Kivera auth0 certificate file
|
58
|
+
config_param :auth0_cert_file, :string, default: ""
|
59
|
+
|
60
|
+
# Kivera auth0 domain
|
61
|
+
config_param :auth0_domain, :string, default: ""
|
62
|
+
|
63
|
+
# custom headers
|
64
|
+
config_param :custom_headers, :hash, :default => nil
|
65
|
+
|
66
|
+
# Switch non-buffered/buffered plugin
|
67
|
+
config_param :bulk_request, :bool, :default => true
|
68
|
+
config_param :buffered, :bool, :default => false
|
69
|
+
# Compress with gzip except for form serializer
|
70
|
+
config_param :compress_request, :bool, :default => false
|
71
|
+
|
72
|
+
config_section :buffer do
|
73
|
+
config_set_default :@type, DEFAULT_BUFFER_TYPE
|
74
|
+
config_set_default :chunk_keys, ['tag']
|
75
|
+
end
|
76
|
+
|
77
|
+
config_section :format do
|
78
|
+
config_set_default :@type, DEFAULT_FORMATTER
|
79
|
+
end
|
80
|
+
|
81
|
+
def configure(conf)
|
82
|
+
compat_parameters_convert(conf, :buffer, :formatter)
|
83
|
+
super
|
84
|
+
|
85
|
+
@ssl_verify_mode = if @ssl_no_verify
|
86
|
+
OpenSSL::SSL::VERIFY_NONE
|
87
|
+
else
|
88
|
+
OpenSSL::SSL::VERIFY_PEER
|
89
|
+
end
|
90
|
+
|
91
|
+
@last_request_time = nil
|
92
|
+
raise Fluent::ConfigError, "'tag' in chunk_keys is required." if !@chunk_key_tag && @buffered
|
93
|
+
|
94
|
+
if @formatter_config = conf.elements('format').first
|
95
|
+
@formatter = formatter_create
|
96
|
+
end
|
97
|
+
|
98
|
+
if @bulk_request
|
99
|
+
class << self
|
100
|
+
alias_method :format, :bulk_request_format
|
101
|
+
end
|
102
|
+
@formatter = formatter_create(type: :json)
|
103
|
+
@serializer = :x_ndjson # secret settings for bulk_request
|
104
|
+
else
|
105
|
+
class << self
|
106
|
+
alias_method :format, :split_request_format
|
107
|
+
end
|
108
|
+
@serializer = :json
|
109
|
+
end
|
110
|
+
|
111
|
+
# Create local storage for persisting JWT token
|
112
|
+
config = conf.elements(name: 'storage').first
|
113
|
+
@storage = storage_create(usage: 'jwt_token', conf: config, default_type: 'local')
|
114
|
+
|
115
|
+
if ! @config_file.empty?
|
116
|
+
creds = File.read(@config_file)
|
117
|
+
parsed = Yajl::Parser.new.parse(StringIO.new(creds))
|
118
|
+
@client_id = parsed.fetch("client_id", @client_id)
|
119
|
+
@client_secret = parsed.fetch("client_secret", @client_secret)
|
120
|
+
@audience = parsed.fetch("audience", @audience)
|
121
|
+
@auth0_cert = parsed.fetch("auth0_cert", @auth0_cert)
|
122
|
+
@auth0_cert_file = parsed.fetch("auth0_cert", @auth0_cert_file)
|
123
|
+
@auth0_domain = parsed.fetch("auth0_domain", @auth0_domain)
|
124
|
+
end
|
125
|
+
|
126
|
+
if @auth0_cert.empty? && ! @auth0_cert_file.empty?
|
127
|
+
@auth0_cert = File.read(@auth0_cert_file)
|
128
|
+
end
|
129
|
+
|
130
|
+
if @client_id.empty? &&
|
131
|
+
@client_secret.empty? &&
|
132
|
+
@audience.empty? &&
|
133
|
+
@auth0_cert.empty? &&
|
134
|
+
@auth0_domain.empty?
|
135
|
+
params = "client_id, client_secret, audience, auth0_cert and auth0_domain"
|
136
|
+
log.error "Missing configuration. Either specify a config_file or set the #{params} parameters"
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
def start
|
142
|
+
super
|
143
|
+
end
|
144
|
+
|
145
|
+
def shutdown
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
149
|
+
def format_url(tag, time, record)
|
150
|
+
@endpoint_url
|
151
|
+
end
|
152
|
+
|
153
|
+
def set_body(req, tag, time, record)
|
154
|
+
if @serializer == :json
|
155
|
+
set_json_body(req, record)
|
156
|
+
elsif @serializer == :x_ndjson
|
157
|
+
set_bulk_body(req, record)
|
158
|
+
else
|
159
|
+
req.set_form_data(record)
|
160
|
+
end
|
161
|
+
req
|
162
|
+
end
|
163
|
+
|
164
|
+
def set_header(req, tag, time, record)
|
165
|
+
if @custom_headers
|
166
|
+
@custom_headers.each do |k,v|
|
167
|
+
req[k] = v
|
168
|
+
end
|
169
|
+
req
|
170
|
+
else
|
171
|
+
req
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def refresh_jwt_token
|
176
|
+
if @storage.get(:jwt_token)
|
177
|
+
if token_expired
|
178
|
+
@storage.put(:jwt_token, new_jwt_token)
|
179
|
+
end
|
180
|
+
else
|
181
|
+
@storage.put(:jwt_token, new_jwt_token)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def token_expired
|
186
|
+
x509 = OpenSSL::X509::Certificate.new(@auth0_cert)
|
187
|
+
begin
|
188
|
+
decoded_token = JWT.decode @storage.get(:jwt_token), x509.public_key, true, { algorithm: 'RS256' }
|
189
|
+
rescue => e
|
190
|
+
log.info 'JWT token expired'
|
191
|
+
return true
|
192
|
+
else
|
193
|
+
if decoded_token[0]['exp'] - Time.now.to_f < TOKEN_EXPIRY_OFFSET
|
194
|
+
log.info 'JWT token about to expire'
|
195
|
+
return true
|
196
|
+
end
|
197
|
+
return false
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def new_jwt_token
|
202
|
+
url = "https://" + @auth0_domain + "/oauth/token"
|
203
|
+
uri = URI.parse(url)
|
204
|
+
req = Net::HTTP::Post.new(uri.to_s)
|
205
|
+
payload = {
|
206
|
+
"client_id" => @client_id,
|
207
|
+
"client_secret" => @client_secret,
|
208
|
+
"audience" => @audience,
|
209
|
+
"grant_type" => "client_credentials"
|
210
|
+
}
|
211
|
+
set_json_body(req, payload)
|
212
|
+
res = https(uri).request(req)
|
213
|
+
case res
|
214
|
+
when Net::HTTPSuccess then
|
215
|
+
parsed = Yajl::Parser.new.parse(StringIO.new(res.body))
|
216
|
+
log.info 'Generated new JWT token'
|
217
|
+
parsed['access_token']
|
218
|
+
else
|
219
|
+
log.warn "Failed to get token for client #{@client_id}"
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def https(uri)
|
224
|
+
Net::HTTP.new(uri.host, uri.port).tap { |http|
|
225
|
+
http.use_ssl = true
|
226
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
227
|
+
}
|
228
|
+
end
|
229
|
+
|
230
|
+
def compress_body(req, data)
|
231
|
+
return unless @compress_request
|
232
|
+
gz = Zlib::GzipWriter.new(StringIO.new)
|
233
|
+
gz << data
|
234
|
+
|
235
|
+
req['Content-Encoding'] = "gzip"
|
236
|
+
req.body = gz.close.string
|
237
|
+
end
|
238
|
+
|
239
|
+
def set_json_body(req, data)
|
240
|
+
req.body = Yajl.dump(data)
|
241
|
+
req['Content-Type'] = 'application/json'
|
242
|
+
compress_body(req, req.body)
|
243
|
+
end
|
244
|
+
|
245
|
+
def set_bulk_body(req, data)
|
246
|
+
req.body = data.to_s
|
247
|
+
req['Content-Type'] = 'application/x-ndjson'
|
248
|
+
compress_body(req, req.body)
|
249
|
+
end
|
250
|
+
|
251
|
+
def set_jwt_auth(req)
|
252
|
+
refresh_jwt_token
|
253
|
+
req['Authorization'] = "Bearer #{@storage.get(:jwt_token)}"
|
254
|
+
end
|
255
|
+
|
256
|
+
def create_request(tag, time, record)
|
257
|
+
url = format_url(tag, time, record)
|
258
|
+
uri = URI.parse(url)
|
259
|
+
req = Net::HTTP::Put.new(uri.request_uri)
|
260
|
+
set_body(req, tag, time, record)
|
261
|
+
set_header(req, tag, time, record)
|
262
|
+
set_jwt_auth(req)
|
263
|
+
return req, uri
|
264
|
+
end
|
265
|
+
|
266
|
+
def http_opts(uri)
|
267
|
+
opts = {
|
268
|
+
:use_ssl => uri.scheme == 'https'
|
269
|
+
}
|
270
|
+
opts[:verify_mode] = @ssl_verify_mode if opts[:use_ssl]
|
271
|
+
opts
|
272
|
+
end
|
273
|
+
|
274
|
+
def proxies
|
275
|
+
ENV['HTTPS_PROXY'] || ENV['HTTP_PROXY'] || ENV['http_proxy'] || ENV['https_proxy']
|
276
|
+
end
|
277
|
+
|
278
|
+
def send_request(req, uri)
|
279
|
+
is_rate_limited = (@rate_limit_msec != 0 and not @last_request_time.nil?)
|
280
|
+
if is_rate_limited and ((Time.now.to_f - @last_request_time) * 1000.0 < @rate_limit_msec)
|
281
|
+
log.info('Dropped request due to rate limiting')
|
282
|
+
return
|
283
|
+
end
|
284
|
+
|
285
|
+
res = nil
|
286
|
+
|
287
|
+
begin
|
288
|
+
|
289
|
+
if proxy = proxies
|
290
|
+
proxy_uri = URI.parse(proxy)
|
291
|
+
|
292
|
+
res = Net::HTTP.start(uri.host, uri.port,
|
293
|
+
proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password,
|
294
|
+
**http_opts(uri)) {|http| http.request(req) }
|
295
|
+
else
|
296
|
+
res = Net::HTTP.start(uri.host, uri.port, **http_opts(uri)) {|http| http.request(req) }
|
297
|
+
end
|
298
|
+
|
299
|
+
rescue => e # rescue all StandardErrors
|
300
|
+
# server didn't respond
|
301
|
+
log.warn "Net::HTTP.#{req.method.capitalize} raises exception: #{e.class}, '#{e.message}'"
|
302
|
+
raise e if @raise_on_error
|
303
|
+
else
|
304
|
+
unless res and res.is_a?(Net::HTTPSuccess)
|
305
|
+
res_summary = if res
|
306
|
+
"#{res.code} #{res.message} #{res.body}"
|
307
|
+
else
|
308
|
+
"res=nil"
|
309
|
+
end
|
310
|
+
if @recoverable_status_codes.include?(res.code.to_i)
|
311
|
+
raise RecoverableResponse, res_summary
|
312
|
+
else
|
313
|
+
log.warn "failed to #{req.method} #{uri} (#{res_summary})"
|
314
|
+
end
|
315
|
+
end #end unless
|
316
|
+
end # end begin
|
317
|
+
end # end send_request
|
318
|
+
|
319
|
+
def handle_record(tag, time, record)
|
320
|
+
if @formatter_config
|
321
|
+
record = @formatter.format(tag, time, record)
|
322
|
+
end
|
323
|
+
req, uri = create_request(tag, time, record)
|
324
|
+
send_request(req, uri)
|
325
|
+
end
|
326
|
+
|
327
|
+
def handle_records(tag, time, chunk)
|
328
|
+
req, uri = create_request(tag, time, chunk.read)
|
329
|
+
send_request(req, uri)
|
330
|
+
end
|
331
|
+
|
332
|
+
def prefer_buffered_processing
|
333
|
+
@buffered
|
334
|
+
end
|
335
|
+
|
336
|
+
def format(tag, time, record)
|
337
|
+
# For safety.
|
338
|
+
end
|
339
|
+
|
340
|
+
def split_request_format(tag, time, record)
|
341
|
+
[time, record].to_msgpack
|
342
|
+
end
|
343
|
+
|
344
|
+
def bulk_request_format(tag, time, record)
|
345
|
+
@formatter.format(tag, time, record)
|
346
|
+
end
|
347
|
+
|
348
|
+
def formatted_to_msgpack_binary?
|
349
|
+
if @bulk_request
|
350
|
+
false
|
351
|
+
else
|
352
|
+
true
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def multi_workers_ready?
|
357
|
+
true
|
358
|
+
end
|
359
|
+
|
360
|
+
def process(tag, es)
|
361
|
+
es.each do |time, record|
|
362
|
+
handle_record(tag, time, record)
|
363
|
+
end
|
364
|
+
end
|
365
|
+
|
366
|
+
def write(chunk)
|
367
|
+
tag = chunk.metadata.tag
|
368
|
+
@endpoint_url = extract_placeholders(@endpoint_url, chunk)
|
369
|
+
if @bulk_request
|
370
|
+
time = Fluent::Engine.now
|
371
|
+
handle_records(tag, time, chunk)
|
372
|
+
else
|
373
|
+
chunk.msgpack_each do |time, record|
|
374
|
+
handle_record(tag, time, record)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|
378
|
+
end
|