fluent-plugin-protobuf-http 0.2.0 → 0.3.1

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
  SHA256:
3
- metadata.gz: ce7ced8f827b15e5e9520ccedf03984720014de458c653ce55a2cb3a8b2c0b4d
4
- data.tar.gz: aec3d4403856f8d5f67f55c143d5e835b0d027dc8ef0599d952d06544a738795
3
+ metadata.gz: a559c0013395560b44bd1a1e2ca12898e481b932b8860f1cf31f557544031458
4
+ data.tar.gz: 827f41577acb0bbf3f526047030a662a3069eaeafe5fa3a648e1027dfb41f2fc
5
5
  SHA512:
6
- metadata.gz: db3abc4b52dd4d9c69bc8b8f493877ce65308e2d4deca4bce4c0888d2b3e1e6d1ee39fb8c0f0ccfb43e0ef5a3367e7883feb4ecb4d104effb5ad26dd549adc3a
7
- data.tar.gz: 36bd9d1a8877f9cd31ae66eacde63b99d89fa6d6adc29e4ed6f5240eaf713a05b1c8b8828945eac84a062fc4e1081030e00b6924eded3e55b8e287edaa0f55da
6
+ metadata.gz: 627665ee19fc340181a6c4e00912aa537b9b2f12bfff6c96c582180453302a6c88c756c583c9c7f003a19c57f9f48d799b912155dae8898e0a0bf54997030d9f
7
+ data.tar.gz: 6b85fda4bb44831b70023728cc8b10071d5a771f753f0c4054fa87a1f9ce650635dfd2a1fa9997c7e13ea798b65df8e874fd247de4d0339af20d7ebac6e93d02
@@ -0,0 +1,6 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: "github-actions"
4
+ directory: "/"
5
+ schedule:
6
+ interval: "weekly"
@@ -0,0 +1,37 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ paths-ignore: ['**.md', '.rubocop.yml']
7
+ pull_request:
8
+ branches: [main]
9
+ types: [opened, synchronize, reopened]
10
+ paths-ignore: ['**.md', '.rubocop.yml']
11
+
12
+ jobs:
13
+ build-and-test:
14
+ strategy:
15
+ matrix:
16
+ os: [ubuntu-latest, macos-latest, windows-latest]
17
+ ruby-version: ['2.6', '2.7']
18
+
19
+ runs-on: ${{ matrix.os }}
20
+
21
+ steps:
22
+ - name: Checkout [${{ github.repository }}]
23
+ uses: actions/checkout@v4
24
+
25
+ - name: Set up Ruby ${{ matrix.ruby-version }}
26
+ uses: ruby/setup-ruby@v1
27
+ with:
28
+ ruby-version: ${{ matrix.ruby-version }}
29
+ bundler-cache: true
30
+
31
+ - name: Install protoc
32
+ uses: arduino/setup-protoc@master
33
+ with:
34
+ repo-token: ${{ secrets.GITHUB_TOKEN }}
35
+
36
+ - name: Run tests
37
+ run: bundle exec rake test
data/.rubocop.yml ADDED
@@ -0,0 +1,19 @@
1
+ # RuboCop Default Config: https://github.com/rubocop-hq/rubocop/blob/master/config/default.yml
2
+
3
+ AllCops:
4
+ NewCops: disable
5
+
6
+ Gemspec/RequiredRubyVersion:
7
+ Enabled: false
8
+
9
+ Metrics/ClassLength:
10
+ Enabled: false
11
+
12
+ Metrics/MethodLength:
13
+ Enabled: false
14
+
15
+ Metrics/AbcSize:
16
+ Enabled: false
17
+
18
+ Layout/LineLength:
19
+ Enabled: false
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen-string-literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  gemspec
data/README.md CHANGED
@@ -1,62 +1,79 @@
1
1
  # fluent-plugin-protobuf-http
2
2
 
3
- [Fluentd](https://fluentd.org/) HTTP input plugin for Protocol Buffers.
3
+ [![ci](https://github.com/iamazeem/fluent-plugin-protobuf-http/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/iamazeem/fluent-plugin-protobuf-http/actions/workflows/ci.yml)
4
+ [![License: Apache](https://img.shields.io/badge/license-Apache-darkgreen.svg?style=flat-square)](https://github.com/iamAzeem/fluent-plugin-protobuf-http/blob/master/LICENSE)
5
+ ![GitHub release (latest by date)](https://img.shields.io/github/v/release/iamAzeem/fluent-plugin-protobuf-http?style=flat-square)
6
+ [![RubyGems Downloads](https://img.shields.io/gem/dt/fluent-plugin-protobuf-http?style=flat-square)](https://rubygems.org/gems/fluent-plugin-protobuf-http)
7
+ [![Buy Me a Coffee](https://img.shields.io/badge/Support-Buy%20Me%20A%20Coffee-orange.svg?style=flat-square)](https://www.buymeacoffee.com/iamazeem)
8
+
9
+ ## Overview
10
+
11
+ [Fluentd](https://fluentd.org/) HTTP input plugin for
12
+ [Protocol Buffers](https://github.com/protocolbuffers/protobuf).
4
13
 
5
14
  ## Features
6
15
 
7
- * **ProtoBuf Schemas**: Automatic compilation of `.proto` files located in `proto_dir`
8
- * **Incoming Message Format**: Support for binary or JSON format (`Content-Type`: `application/octet-stream` or `application/json`)
9
- * **Outgoing Message Format**: Support for binary or JSON format
10
- * **Message Types**: Single or Batch
11
- * **TLS Support**: Use `<transport tls> ... </transport>` section in configuration and `https://` URL protocol prefix. See this [example](https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-server#configuration-example) for more details.
16
+ - Automatic compilation of `.proto` files located in `proto_dir`
17
+ - Incoming Format: Binary or JSON (`Content-Type`: `application/octet-stream` or
18
+ `application/json`)
19
+ - Outgoing Format: Binary or JSON
20
+ - Single and Batch message support
21
+ - TLS Support with `<transport>` section and `https://` URL protocol prefix.
22
+
23
+ For more details on TLS configuration, see this official
24
+ [example](https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-server#configuration-example).
12
25
 
13
26
  ## Installation
14
27
 
15
28
  ### RubyGems
16
29
 
17
- ```
18
- $ gem install fluent-plugin-protobuf-http
30
+ ```shell
31
+ gem install fluent-plugin-protobuf-http
19
32
  ```
20
33
 
21
34
  ### Bundler
22
35
 
23
- Add following line to your Gemfile:
36
+ Add the following line to your Gemfile:
37
+
24
38
  ```ruby
25
- gem "fluent-plugin-protobuf-http"
39
+ gem 'fluent-plugin-protobuf-http'
26
40
  ```
27
41
 
28
42
  And then execute:
29
- ```
30
- $ bundle
43
+
44
+ ```shell
45
+ bundle
31
46
  ```
32
47
 
33
48
  ## Configuration
34
49
 
35
- * **bind** (string) (optional): The address to listen to.
36
- * Default value: `0.0.0.0`.
37
- * **port** (integer) (optional): The port to listen to.
38
- * Default value: `8080`.
39
- * **proto_dir** (string) (required): The directory path that contains the .proto files.
40
- * **in_mode** (enum) (optional): The mode of incoming (supported) events.
41
- * Available values: binary, json
42
- * Default value: `binary`.
43
- * **out_mode** (enum) (optional): The mode of outgoing (emitted) events.
44
- * Available values: binary, json
45
- * Default value: `binary`.
46
- * **tag** (string) (required): The tag for the event.
47
-
48
- ### \<transport\> section (optional) (single)
49
-
50
- * **protocol** (enum) (optional):
51
- * Available values: tcp, tls
52
- * Default value: `tcp`.
53
- * See [example](https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-server#configuration-example).
50
+ - `bind` (string) (optional): The address to listen to.
51
+ - Default: `0.0.0.0`
52
+ - `port` (integer) (optional): The port to listen to.
53
+ - Default: `8080`
54
+ - `proto_dir` (string) (required): The directory path that contains the .proto files.
55
+ - `in_mode` (enum) (optional): The mode of incoming (supported) events.
56
+ - Modes: `binary`, `json`
57
+ - Default: `binary`
58
+ - `out_mode` (enum) (optional): The mode of outgoing (emitted) events.
59
+ - Modes: `binary`, `json`
60
+ - Default: `binary`
61
+ - `tag` (string) (required): The tag for the event.
62
+
63
+ ### `<transport>` section (optional) (single)
64
+
65
+ - `protocol` (enum) (optional):
66
+ - Protocols: `tcp`, `tls`
67
+ - Default: `tcp`
68
+ - For more details, see this official configuration
69
+ [example](https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-server#configuration-example).
54
70
 
55
71
  ### Example
56
72
 
57
- ```
58
- # Single Message: http://ip:port/<tag>?msgtype=<msgtype>
59
- # Batch Message: http://ip:port/<tag>?msgtype=<batch-msgtype>?batch=true
73
+ ```text
74
+ # Endpoints:
75
+ # - Single Message: http://ip:port/<tag>?msgtype=<msgtype>
76
+ # - Batch Message: http://ip:port/<tag>?msgtype=<batch-msgtype>?batch=true
60
77
 
61
78
  <source>
62
79
  @type protobuf_http
@@ -74,12 +91,18 @@ $ bundle
74
91
 
75
92
  ## Schemas (`.proto` files)
76
93
 
77
- The logging of events is assumed to be the prime use-case for this plugin.
78
- So, use self-contained `.proto` file(s) that don't import other custom `.proto` file(s).
79
- The `package` and `message` names must be unique and are treated as case-sensitive.
94
+ The prime use-case for this plugin is assumed to be event logging. So, always
95
+ use self-contained `.proto` file(s) that do not import other `.proto` files. The
96
+ names e.g. `package`, `message`, etc. must be unique and are treated as
97
+ case-sensitive.
80
98
 
81
- Consider this [`log.proto`](https://github.com/iamAzeem/protobuf-log-sample/blob/master/log.proto) schema:
82
- ```
99
+ Consider this
100
+ [log.proto](https://github.com/iamAzeem/protobuf-log-sample/blob/master/log.proto)
101
+ schema from
102
+ [protobuf-log-sample](https://github.com/iamAzeem/protobuf-log-sample)
103
+ repository:
104
+
105
+ ```protobuf
83
106
  syntax = "proto3";
84
107
 
85
108
  package service.logging;
@@ -108,9 +131,9 @@ message Log {
108
131
  }
109
132
  ```
110
133
 
111
- The fully-qualified message type for `Log` will be `service.logging.Log`.
112
- This message type is used as the value of `msgtype` query parameter in the URL.
113
- See URL section below for more on `msgtype`.
134
+ The fully-qualified message type for `Log` is `service.logging.Log`. This
135
+ message type is used as the value of `msgtype` query parameter in the URL. See
136
+ URL section below for more on `msgtype`.
114
137
 
115
138
  ### Single Message
116
139
 
@@ -118,20 +141,21 @@ The above schema will be used as-is for the single message.
118
141
 
119
142
  ### Batch Message
120
143
 
121
- For a batch, the schema must be like this:
122
- ```
144
+ For the batch message, the schema must be like this:
145
+
146
+ ```protobuf
123
147
  message Batch {
124
148
  string type = 1;
125
149
  repeated Log batch = 2;
126
150
  }
127
151
  ```
128
152
 
129
- IMPORTANT:
130
- The `Batch` message type is part of `log.proto`, it's not a separate file!
131
- You can choose any name for a batch message type.
153
+ **IMPORTANT**: The `Batch` message type is part of `log.proto`, it is not a
154
+ separate file! You can choose any name for a batch message type.
132
155
 
133
- The complete `log.proto` will be:
134
- ```
156
+ Here is the complete `log.proto` file:
157
+
158
+ ```protobuf
135
159
  syntax = "proto3";
136
160
 
137
161
  package service.logging;
@@ -165,50 +189,62 @@ message Batch {
165
189
  }
166
190
  ```
167
191
 
168
- For batch processing, the plugin looks for special members `type` and `batch`.
192
+ For batch processing, the plugin looks for special members `type` and `batch`.
169
193
  The `type` will indicate the message type of `batch` i.e. `Log` in this example.
170
194
 
171
- The type of `Batch` is `service.logging.Batch` and it will be the value of `msgtype` in the URL query.
172
- The type of `batch` array is `service.logging.Log` and it will be the value of `type`.
195
+ The type of `Batch` is `service.logging.Batch` and it will be the value of
196
+ `msgtype` in the URL query. The type of `batch` array is `service.logging.Log`
197
+ and it will be the value of `type`.
173
198
 
174
- The `google.protobuf.Any` type has not been used deliberately here.
175
- It stores message type information with each message resulting in increase in size.
176
- With the above approach, the type is stored only once for the whole batch.
199
+ The `google.protobuf.Any` type has not been used here deliberately. It stores
200
+ the message type with each message resulting in an increase in size. Refer to
201
+ [protobuf-repeated-type-vs-any](https://github.com/iamAzeem/protobuf-repeated-type-vs-any)
202
+ for a simple comparison. With the above approach, the type is stored only once
203
+ for the whole batch.
177
204
 
178
205
  ### Endpoint (URL)
179
206
 
180
207
  For single message:
181
- ```
208
+
209
+ ```text
182
210
  http://<ip>:<port>/<tag>?msgtype=<fully-qualified-message-type>
183
211
  ```
184
212
 
185
213
  For batch message:
186
- ```
214
+
215
+ ```text
187
216
  http://<ip>:<port>/<tag>?msgtype=<fully-qualified-message-type-for-batch>&batch=true
188
217
  ```
189
218
 
190
- Without `batch=true` query parameter, the batch will be treated as a single message.
219
+ Without `batch=true` query parameter, the batch will be treated as a single
220
+ message.
191
221
 
192
- For example, for a log type `service.logging.Log` and its corresponding batch type `service.logging.Batch`:
222
+ For example, for a log type `service.logging.Log` and its corresponding batch
223
+ type `service.logging.Batch`, the URLs would be:
193
224
 
194
- Single:
195
- ```
225
+ For single message:
226
+
227
+ ```text
196
228
  http://localhost:8080/debug.test?msgtype=service.logging.Log
197
229
  ```
198
230
 
199
- Batch:
200
- ```
231
+ For batch message:
232
+
233
+ ```text
201
234
  http://localhost:8080/debug.test?msgtype=service.logging.Batch&batch=true
202
235
  ```
203
236
 
204
- **NOTE**: The values of query parameters (`msgtype`, `batch`) are case-sensitive!
237
+ **NOTE**: The values of query parameters (`msgtype`, `batch`) are
238
+ case-sensitive!
205
239
 
206
240
  ## Test Use-Case (`curl`)
207
241
 
208
- For a simple test use-case of events and their routing to [stdout](https://docs.fluentd.org/output/stdout) can be configured like this:
242
+ For a simple use-case of incoming HTTP events and their routing to
243
+ [stdout](https://docs.fluentd.org/output/stdout) may be configured like this:
209
244
 
210
245
  `fluent.conf`:
211
- ```
246
+
247
+ ```text
212
248
  <source>
213
249
  @type protobuf_http
214
250
  @id protobuf_http_input
@@ -224,24 +260,36 @@ For a simple test use-case of events and their routing to [stdout](https://docs.
224
260
 
225
261
  <match debug.test>
226
262
  @type stdout
227
- @id stdout_output
228
263
  </match>
229
264
  ```
230
265
 
231
- The incoming binary messages will be transformed to JSON for further consumption.
266
+ The incoming binary messages will be converted to JSON for further consumption.
232
267
 
233
- #### Single Message
268
+ ### Single Message Use-case
234
269
 
235
- Test Input Parameters:
236
- * data: `log.bin`, msgtype: `service.logging.Log`
270
+ Test Parameters:
237
271
 
238
- Command:
239
- ```
240
- $ curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@/<path>/log.bin" "http://localhost:8080/debug.test?msgtype=service.logging.Log"
272
+ | input file | single message type |
273
+ |:----------:|:---------------------:|
274
+ | `log.bin` | `service.logging.Log` |
275
+
276
+ URL:
277
+
278
+ ```bash
279
+ http://localhost:8080/debug.test?msgtype=service.logging.Log
241
280
  ```
242
281
 
243
- `fluentd` Logs (Observe JSON at the end):
282
+ `curl` command:
283
+
284
+ ```shell
285
+ curl -X POST -H "Content-Type: application/octet-stream" \
286
+ --data-binary "@/<path>/log.bin" \
287
+ "http://localhost:8080/debug.test?msgtype=service.logging.Log"
244
288
  ```
289
+
290
+ `fluentd` logs (Observe JSON at the end):
291
+
292
+ ```text
245
293
  2020-06-09 18:53:47 +0500 [info]: #0 [protobuf_http_input] [R] {binary} [127.0.0.1:41222, size: 86 bytes]
246
294
  2020-06-09 18:53:47 +0500 [warn]: #0 [protobuf_http_input] 'batch' not found in 'query_string' [msgtype=service.logging.Log]
247
295
  2020-06-09 18:53:47 +0500 [info]: #0 [protobuf_http_input] [S] {binary} [127.0.0.1:41222, msgtype: service.logging.Log, size: 86 bytes]
@@ -249,22 +297,38 @@ $ curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@/<pat
249
297
  2020-06-09 18:53:47 +0500 [info]: #0 [protobuf_http_input] [S] {json} [127.0.0.1:41222, msgtype: service.logging.Log, size: 183 bytes]
250
298
  ```
251
299
 
252
- For Test Single Message Generation: https://github.com/iamAzeem/protobuf-log-sample
300
+ For generating sample Single messages, see
301
+ https://github.com/iamAzeem/protobuf-log-sample.
253
302
 
254
- #### Batch Message
303
+ ### Batch Message Use-case
255
304
 
256
- Test Input Parameters:
257
- * data: `logbatch2.bin`, msgtype: `service.logging.Batch`, type: `service.logging.Log` [batch_size: 2 messages]
258
- * data: `logbatch5.bin`, msgtype: `service.logging.Batch`, type: `service.logging.Log` [batch_size: 5 messages]
305
+ Test Parameters:
259
306
 
260
- Command (`logbatch2.bin`):
307
+ | input file | batch message type | batch internal type | messages |
308
+ |:---------------:|:-----------------------:|:---------------------:|:--------:|
309
+ | `logbatch2.bin` | `service.logging.Batch` | `service.logging.Log` | 2 |
310
+ | `logbatch5.bin` | `service.logging.Batch` | `service.logging.Log` | 5 |
311
+
312
+ URL:
313
+
314
+ ```text
315
+ http://localhost:8080/debug.test?msgtype=service.logging.Batch&batch=true
261
316
  ```
262
- $ curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@/<path>/logbatch2.bin" "http://localhost:8080/debug.test?msgtype=service.logging.Batch&batch=true"
317
+
318
+ **`logbatch2.bin`**
319
+
320
+ `curl` command:
321
+
322
+ ```shell
323
+ $ curl -X POST -H "Content-Type: application/octet-stream" \
324
+ --data-binary "@/<path>/logbatch2.bin" \
325
+ "http://localhost:8080/debug.test?msgtype=service.logging.Batch&batch=true"
263
326
  {"status":"Batch received! [batch_type: service.logging.Log, batch_size: 2 messages]"}
264
327
  ```
265
328
 
266
- `fluentd` Logs:
267
- ```
329
+ `fluentd` logs:
330
+
331
+ ```text
268
332
  2020-06-09 19:04:13 +0500 [info]: #0 [protobuf_http_input] [R] {binary} [127.0.0.1:41416, size: 207 bytes]
269
333
  2020-06-09 19:04:13 +0500 [info]: #0 [protobuf_http_input] [B] {binary} [127.0.0.1:41416, msgtype: service.logging.Batch, size: 207 bytes]
270
334
  2020-06-09 19:04:13 +0500 [info]: #0 [protobuf_http_input] [B] Emitting message stream/batch [batch_size: 2 messages]...
@@ -273,14 +337,20 @@ $ curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@/<pat
273
337
  2020-06-09 19:04:13 +0500 [info]: #0 [protobuf_http_input] [B] {json} [127.0.0.1:41416, msgtype: service.logging.Batch] Batch received! [batch_type: service.logging.Log, batch_size: 2 messages]
274
338
  ```
275
339
 
276
- Command (`logbatch5.bin`):
277
- ```
278
- $ curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@/<path>/logbatch5.bin" "http://localhost:8080/debug.test?msgtype=service.logging.Batch&batch=true"
340
+ **`logbatch5.bin`**
341
+
342
+ `curl` command:
343
+
344
+ ```bash
345
+ $ curl -X POST -H "Content-Type: application/octet-stream" \
346
+ --data-binary "@/<path>/logbatch5.bin" \
347
+ "http://localhost:8080/debug.test?msgtype=service.logging.Batch&batch=true"
279
348
  {"status":"Batch received! [batch_type: service.logging.Log, batch_size: 5 messages]"}
280
349
  ```
281
350
 
282
- `fluentd` Logs:
283
- ```
351
+ `fluentd` logs:
352
+
353
+ ```text
284
354
  2020-06-09 19:07:09 +0500 [info]: #0 [protobuf_http_input] [R] {binary} [127.0.0.1:41552, size: 486 bytes]
285
355
  2020-06-09 19:07:09 +0500 [info]: #0 [protobuf_http_input] [B] {binary} [127.0.0.1:41552, msgtype: service.logging.Batch, size: 486 bytes]
286
356
  2020-06-09 19:07:09 +0500 [info]: #0 [protobuf_http_input] [B] Emitting message stream/batch [batch_size: 5 messages]...
@@ -292,10 +362,48 @@ $ curl -X POST -H "Content-Type: application/octet-stream" --data-binary "@/<pat
292
362
  2020-06-09 19:07:09 +0500 [info]: #0 [protobuf_http_input] [B] {json} [127.0.0.1:41552, msgtype: service.logging.Batch] Batch received! [batch_type: service.logging.Log, batch_size: 5 messages]
293
363
  ```
294
364
 
295
- For Test Batch Message Generation: https://gist.github.com/iamAzeem/a8a24092132e1741a76956192f2104cc
365
+ For sample Batch message generation, see
366
+ [this gist](https://gist.github.com/iamAzeem/a8a24092132e1741a76956192f2104cc).
367
+
368
+ ## CI Workflow
369
+
370
+ The [CI workflow](ci..github/workflows/ci.yml) sets up the prerequisites. It
371
+ builds and installs the plugin, and then runs the automated tests.
372
+
373
+ To run tests locally, run:
374
+
375
+ ```shell
376
+ bundle exec rake test
377
+ ```
378
+
379
+ The [test](./test) directory contains the tests and the [input](./test/data)
380
+ files.
381
+
382
+ The code coverage is printed at the end using `simplecov`.
383
+
384
+ ## Known Issues
385
+
386
+ - This plugin internally uses the HTTP server plugin
387
+ [helper](https://docs.fluentd.org/plugin-helper-overview/api-plugin-helper-http_server)
388
+ which has higher precedence for `async-http` over `webrick`. But,
389
+ [`webrick`](https://github.com/ruby/webrick) is required to run this. In an
390
+ environment where both are installed, `async-http` is automatically selected
391
+ causing runtime issues. To make this work, you need to uninstall `async-http`
392
+ i.e. `gem uninstall async-http`. See issue
393
+ [#10](https://github.com/iamazeem/fluent-plugin-protobuf-http/issues/10) for
394
+ more details where this was identified in Docker containers where both gems
395
+ were already installed.
396
+
397
+ ## Contribute
398
+
399
+ - [Fork](https://github.com/iamazeem/fluent-plugin-protobuf-http/fork) the project.
400
+ - Check out the latest `main` branch.
401
+ - Create a `feature` or `bugfix` branch from `main`.
402
+ - Commit and push your changes.
403
+ - Make sure to add and run tests locally: `bundle exec rake test`.
404
+ - Run [Rubocop](https://github.com/rubocop/rubocop) and fix the lint errors.
405
+ - Submit the PR.
296
406
 
297
- ## Copyright
407
+ ## License
298
408
 
299
- * Copyright&copy; 2020 [Azeem Sajid](https://www.linkedin.com/in/az33msajid/)
300
- * License
301
- * Apache License, Version 2.0
409
+ [Apache 2.0](LICENSE)
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen-string-literal: true
2
+
1
3
  require 'bundler'
2
4
  Bundler::GemHelper.install_tasks
3
5
 
@@ -1,9 +1,12 @@
1
+ # frozen-string-literal: true
2
+
1
3
  lib = File.expand_path('../lib', __dir__)
2
4
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
5
 
4
6
  Gem::Specification.new do |spec|
5
7
  spec.name = 'fluent-plugin-protobuf-http'
6
- spec.version = '0.2.0'
8
+ spec.version = '0.3.1'
9
+ spec.date = '2023-09-16'
7
10
  spec.authors = ['Azeem Sajid']
8
11
  spec.email = ['azeem.sajid@gmail.com']
9
12
 
@@ -20,8 +23,9 @@ Gem::Specification.new do |spec|
20
23
  spec.test_files = test_files
21
24
  spec.require_paths = ['lib']
22
25
 
23
- spec.add_development_dependency 'bundler', '~> 1.14'
26
+ spec.add_development_dependency 'bundler', '~> 2.1', '>= 2.1.0'
24
27
  spec.add_development_dependency 'rake', '~> 12.0'
28
+ spec.add_development_dependency 'simplecov', '~> 0.12', '<= 0.12.2'
25
29
  spec.add_development_dependency 'test-unit', '~> 3.0'
26
30
  spec.add_runtime_dependency 'fluentd', ['>= 0.14.10', '< 2']
27
31
  spec.add_runtime_dependency 'google-protobuf', '~> 3.12', '>= 3.12.2'
@@ -1,3 +1,5 @@
1
+ # frozen-string-literal: true
2
+
1
3
  #
2
4
  # Copyright 2020 Azeem Sajid
3
5
  #
@@ -18,9 +20,11 @@ require 'fluent/config/error'
18
20
  require 'fluent/plugin_helper/http_server'
19
21
  require 'webrick/httputils'
20
22
  require 'json'
23
+ require 'English'
21
24
 
22
25
  module Fluent
23
26
  module Plugin
27
+ # Implementation of HTTP input plugin for Protobuf
24
28
  class ProtobufHttpInput < Fluent::Plugin::Input
25
29
  Fluent::Plugin.register_input('protobuf_http', self)
26
30
 
@@ -46,10 +50,6 @@ module Fluent
46
50
  config_argument :protocol, :enum, list: %i[tcp tls], default: :tcp
47
51
  end
48
52
 
49
- config_section :transform, required: false, multi: false, init: true, param_name: :transform_config do
50
- config_argument :msgtype, :string
51
- end
52
-
53
53
  def initialize
54
54
  super
55
55
 
@@ -61,16 +61,16 @@ module Fluent
61
61
  def compile_protos
62
62
  log.debug("Checking proto_dir [#{@proto_dir}]...")
63
63
 
64
- path = File.expand_path(@proto_dir).freeze
64
+ path = File.expand_path(@proto_dir)
65
65
  raise Fluent::ConfigError, "protos_dir does not exist! [#{path}]" unless Dir.exist?(path)
66
66
 
67
- @protos = Dir["#{path}/*.proto"].freeze
67
+ @protos = Dir["#{path}/*.proto"]
68
68
  raise Fluent::ConfigError, "Empty proto_dir! [#{path}]" unless @protos.any?
69
69
 
70
70
  log.info("Compiling .proto files [#{@protos.length}]...")
71
71
 
72
72
  `protoc --ruby_out=#{path} --proto_path=#{path} #{path}/*.proto`
73
- raise Fluent::ConfigError, 'Could not compile! See error(s) above.' unless $?.success?
73
+ raise Fluent::ConfigError, 'Could not compile! See error(s) above.' unless $CHILD_STATUS.success?
74
74
 
75
75
  log.info("Compiled successfully:\n- #{@protos.join("\n- ")}")
76
76
 
@@ -82,8 +82,8 @@ module Fluent
82
82
  end
83
83
 
84
84
  def get_compiled_proto(proto)
85
- proto_suffix = '.proto'.freeze
86
- compiled_proto_suffix = '_pb.rb'.freeze
85
+ proto_suffix = '.proto'
86
+ compiled_proto_suffix = '_pb.rb'
87
87
 
88
88
  compiled_proto = proto.chomp(proto_suffix) + compiled_proto_suffix
89
89
  raise Fluent::ConfigError, "Compiled proto not found! [#{compiled_proto}]" unless File.file?(compiled_proto)
@@ -116,9 +116,10 @@ module Fluent
116
116
  log.debug("Extracting message types [#{compiled_proto}]...")
117
117
  msg_types = []
118
118
  File.foreach(compiled_proto) do |line|
119
- if line.lstrip.start_with?('add_message')
120
- msg_type = line[/"([^"]*)"/, 1].freeze # regex: <add_message> 'msg_type' <do>
121
- msg_types.push(msg_type) unless msg_type.nil?
119
+ line.strip!
120
+ if line.include?('::Google::Protobuf::DescriptorPool.generated_pool.lookup') && line.end_with?('.msgclass')
121
+ extracted_msg_type = line[/"([^"]*)"/, 1].freeze
122
+ msg_types.push(extracted_msg_type) unless extracted_msg_type.nil?
122
123
  end
123
124
  end
124
125
 
@@ -152,15 +153,13 @@ module Fluent
152
153
  tls_opts = @transport_config.to_h
153
154
  end
154
155
 
155
- log.warn("#{@transform_config.to_h}")
156
-
157
156
  log.info("Starting protobuf #{proto == :tcp ? 'HTTP' : 'HTTPS'} server [#{@bind}:#{@port}]...")
158
157
  log.debug("TLS configuration:\n#{tls_opts}") if tls_opts
159
158
 
160
159
  http_server_create_http_server(:protobuf_server, addr: @bind, port: @port, logger: log, proto: proto, tls_opts: tls_opts) do |server|
161
160
  server.post("/#{tag}") do |req|
162
- peeraddr = "#{req.peeraddr[2]}:#{req.peeraddr[1]}".freeze # ip:port
163
- serialized_msg = req.body.freeze
161
+ peeraddr = "#{req.peeraddr[2]}:#{req.peeraddr[1]}" # ip:port
162
+ serialized_msg = req.body
164
163
 
165
164
  log.info("[R] {#{@in_mode}} [#{peeraddr}, size: #{serialized_msg.length} bytes]")
166
165
  log.debug("Dumping serialized message [#{serialized_msg.length} bytes]:\n#{serialized_msg}")
@@ -168,7 +167,7 @@ module Fluent
168
167
  content_type = req.header['content-type'][0]
169
168
 
170
169
  unless valid_content_type?(content_type)
171
- status = "Invalid 'Content-Type' header! [#{content_type}]".freeze
170
+ status = "Invalid 'Content-Type' header! [#{content_type}]"
172
171
  log.warn("[X] Message rejected! [#{peeraddr}] #{status}")
173
172
  next [400, { 'Content-Type' => 'application/json', 'Connection' => 'close' }, { 'status' => status }.to_json]
174
173
  end
@@ -177,7 +176,7 @@ module Fluent
177
176
 
178
177
  msgtype, batch = get_query_params(req.query_string)
179
178
  unless @msgclass_lookup.key?(msgtype)
180
- status = "Invalid 'msgtype' in 'query_string'! [#{msgtype}]".freeze
179
+ status = "Invalid 'msgtype' in 'query_string'! [#{msgtype}]"
181
180
  log.warn("[X] Message rejected! [#{peeraddr}] #{status}")
182
181
  next [400, { 'Content-Type' => 'application/json', 'Connection' => 'close' }, { 'status' => status }.to_json]
183
182
  end
@@ -187,7 +186,7 @@ module Fluent
187
186
  deserialized_msg = deserialize_msg(msgtype, serialized_msg)
188
187
 
189
188
  if deserialized_msg.nil?
190
- status = "Incompatible message! [msgtype: #{msgtype}, size: #{serialized_msg.length} bytes]".freeze
189
+ status = "Incompatible message! [msgtype: #{msgtype}, size: #{serialized_msg.length} bytes]"
191
190
  log.warn("[X] Message rejected! [#{peeraddr}] #{status}")
192
191
  next [400, { 'Content-Type' => 'application/json', 'Connection' => 'close' }, { 'status' => status }.to_json]
193
192
  end
@@ -214,7 +213,7 @@ module Fluent
214
213
  log.info("[B] {#{@in_mode}} [#{peeraddr}, msgtype: #{msgtype}, size: #{serialized_msg.length} bytes]")
215
214
 
216
215
  if deserialized_msg.type.nil? || deserialized_msg.batch.nil? || deserialized_msg.batch.empty?
217
- status = "Invalid 'batch' message! [msgtype: #{msgtype}, size: #{serialized_msg.length} bytes]".freeze
216
+ status = "Invalid 'batch' message! [msgtype: #{msgtype}, size: #{serialized_msg.length} bytes]"
218
217
  log.warn("[X] Message rejected! [#{peeraddr}] #{status}")
219
218
  next [400, { 'Content-Type' => 'application/json', 'Connection' => 'close' }, { 'status' => status }.to_json]
220
219
  end
@@ -234,7 +233,7 @@ module Fluent
234
233
 
235
234
  router.emit_stream(@tag, stream)
236
235
 
237
- status = "Batch received! [batch_type: #{batch_type}, batch_size: #{batch_size} messages]".freeze
236
+ status = "Batch received! [batch_type: #{batch_type}, batch_size: #{batch_size} messages]"
238
237
  log.info("[B] {#{@out_mode}} [#{peeraddr}, msgtype: #{msgtype}] #{status}")
239
238
  [200, { 'Content-Type' => 'application/json', 'Connection' => 'close' }, { 'status' => status }.to_json]
240
239
  end
@@ -242,8 +241,8 @@ module Fluent
242
241
  end
243
242
 
244
243
  def valid_content_type?(content_type)
245
- hdr_binary = 'application/octet-stream'.freeze
246
- hdr_json = 'application/json'.freeze
244
+ hdr_binary = 'application/octet-stream'
245
+ hdr_json = 'application/json'
247
246
 
248
247
  case @in_mode
249
248
  when :binary
@@ -284,7 +283,7 @@ module Fluent
284
283
  rescue Google::Protobuf::ParseError => e
285
284
  log.error("Incompatible message! [msgtype: #{msgtype}, size: #{serialized_msg.length} bytes] #{e}")
286
285
  nil
287
- rescue => e
286
+ rescue StandardError => e
288
287
  log.error("Deserializaton failed! Error: #{e}")
289
288
  nil
290
289
  end
@@ -300,7 +299,7 @@ module Fluent
300
299
  when :json
301
300
  msgclass.encode_json(deserialized_msg)
302
301
  end
303
- rescue => e
302
+ rescue StandardError => e
304
303
  log.error("Serialization failed! [msgtype: #{msgtype}, msg: #{deserialized_msg}] Error: #{e}")
305
304
  nil
306
305
  end
data/test/data/log.bin ADDED
@@ -0,0 +1,3 @@
1
+
2
+ %
3
+ ����192.168.xxx.xxxtest"test+This is a test log generated by [./log.rb].
@@ -0,0 +1 @@
1
+ {"context":{"timestamp":"2020-06-01T16:24:19Z","hostOrIp":"192.168.xxx.xxx","serviceName":"test","user":"test"},"level":"INFO","message":"This is a test log generated by [./log.rb]."}
@@ -0,0 +1,12 @@
1
+
2
+ service.logging.Log[
3
+ %
4
+ ����192.168.xxx.xxxtest"test0This is a test log generated by [./logbatch.rb].[
5
+ %
6
+ ����192.168.xxx.xxxtest"test0This is a test log generated by [./logbatch.rb].[
7
+ %
8
+ ����192.168.xxx.xxxtest"test0This is a test log generated by [./logbatch.rb].[
9
+ %
10
+ ����192.168.xxx.xxxtest"test0This is a test log generated by [./logbatch.rb].[
11
+ %
12
+ ����192.168.xxx.xxxtest"test0This is a test log generated by [./logbatch.rb].
@@ -0,0 +1,31 @@
1
+ syntax = "proto3";
2
+
3
+ package service.logging;
4
+
5
+ import "google/protobuf/timestamp.proto";
6
+
7
+ message Log {
8
+ message Context {
9
+ google.protobuf.Timestamp timestamp = 1;
10
+ string host_or_ip = 2;
11
+ string service_name = 3;
12
+ string user = 4;
13
+ }
14
+
15
+ enum Level {
16
+ DEBUG = 0;
17
+ INFO = 1;
18
+ WARN = 2;
19
+ ERROR = 3;
20
+ FATAL = 4;
21
+ }
22
+
23
+ Context context = 1;
24
+ Level level = 2;
25
+ string message = 3;
26
+ }
27
+
28
+ message Batch {
29
+ string type = 1;
30
+ repeated Log batch = 2;
31
+ }
data/test/helper.rb CHANGED
@@ -1,8 +1,13 @@
1
- $LOAD_PATH.unshift(File.expand_path("../../", __FILE__))
2
- require "test-unit"
3
- require "fluent/test"
4
- require "fluent/test/driver/input"
5
- require "fluent/test/helpers"
1
+ # frozen-string-literal: true
2
+
3
+ require 'simplecov'
4
+ SimpleCov.start
5
+
6
+ $LOAD_PATH.unshift(File.expand_path('..', __dir__))
7
+ require 'test-unit'
8
+ require 'fluent/test'
9
+ require 'fluent/test/driver/input'
10
+ require 'fluent/test/helpers'
6
11
 
7
12
  Test::Unit::TestCase.include(Fluent::Test::Helpers)
8
13
  Test::Unit::TestCase.extend(Fluent::Test::Helpers)
@@ -1,18 +1,152 @@
1
- require "helper"
2
- require "fluent/plugin/in_protobuf_http.rb"
1
+ # frozen-string-literal: true
3
2
 
3
+ require 'helper'
4
+ require 'fluent/plugin/in_protobuf_http'
5
+ require 'net/http'
6
+
7
+ # Implementation of Test Class
4
8
  class ProtobufHttpInputTest < Test::Unit::TestCase
5
9
  setup do
6
10
  Fluent::Test.setup
11
+ @log_bin = File.open('./test/data/log.bin', 'rb') { |f| f.read }
12
+ @log_json = File.open('./test/data/log.json', 'r') { |f| f.read }
13
+ @log_bin_batch = File.open('./test/data/logbatch5.bin', 'rb') { |f| f.read }
14
+ end
15
+
16
+ def create_driver(conf)
17
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::ProtobufHttpInput).configure(conf)
7
18
  end
8
19
 
9
- test "failure" do
10
- flunk
20
+ sub_test_case 'configure' do
21
+ test 'test default configuration' do
22
+ conf = %(
23
+ proto_dir ./test/data/protos
24
+ tag test
25
+ )
26
+ driver = create_driver(conf)
27
+ plugin = driver.instance
28
+ assert_equal plugin.class, Fluent::Plugin::ProtobufHttpInput
29
+ assert_equal plugin.bind, '0.0.0.0'
30
+ assert_equal plugin.port, 8080
31
+ assert_equal plugin.in_mode, :binary
32
+ assert_equal plugin.out_mode, :binary
33
+ end
34
+ end
35
+
36
+ sub_test_case 'route#emit' do
37
+ conf = %(
38
+ proto_dir ./test/data/protos
39
+ tag test
40
+ in_mode binary
41
+ out_mode json
42
+ )
43
+
44
+ test 'test invalid msgtype in query string i.e. empty or mismatch' do
45
+ driver = create_driver(conf)
46
+ res_codes = []
47
+ driver.run do
48
+ path = '/test'
49
+ res = post(path, @log_bin)
50
+ res_codes << res.code
51
+ end
52
+ assert_equal 1, res_codes.size
53
+ assert_equal '400', res_codes[0]
54
+ end
55
+
56
+ test 'test incoming type mismatch [in_mode != Content-Type]' do
57
+ conf = %(
58
+ proto_dir ./test/data/protos
59
+ tag test
60
+ in_mode binary
61
+ out_mode json
62
+ )
63
+ driver = create_driver(conf)
64
+ res_codes = []
65
+ driver.run do
66
+ path = '/test?msgtype=service.logging.Log'
67
+ res = post(path, @log_bin, :json)
68
+ res_codes << res.code
69
+ end
70
+ assert_equal 1, res_codes.size
71
+ assert_equal '400', res_codes[0]
72
+ end
73
+
74
+ test 'test single message (Binary to JSON)' do
75
+ driver = create_driver(conf)
76
+ res_codes = []
77
+ driver.run do
78
+ path = '/test?msgtype=service.logging.Log'
79
+ res = post(path, @log_bin)
80
+ res_codes << res.code
81
+ end
82
+ assert_equal 1, res_codes.size
83
+ assert_equal '200', res_codes[0]
84
+ end
85
+
86
+ test 'test single message (JSON to Binary)' do
87
+ conf = %(
88
+ proto_dir ./test/data/protos
89
+ tag test
90
+ in_mode json
91
+ out_mode binary
92
+ )
93
+ driver = create_driver(conf)
94
+ res_codes = []
95
+ driver.run do
96
+ path = '/test?msgtype=service.logging.Log'
97
+ res = post(path, @log_json, :json)
98
+ res_codes << res.code
99
+ end
100
+ assert_equal 1, res_codes.size
101
+ assert_equal '200', res_codes[0]
102
+ end
103
+
104
+ test 'test batch messages (Binary to JSON)' do
105
+ conf = %(
106
+ proto_dir ./test/data/protos
107
+ tag test
108
+ in_mode binary
109
+ out_mode json
110
+ )
111
+ driver = create_driver(conf)
112
+ res_codes = []
113
+ driver.run do
114
+ path = '/test?msgtype=service.logging.Batch&batch=true'
115
+ res = post(path, @log_bin_batch)
116
+ res_codes << res.code
117
+ end
118
+ assert_equal 1, res_codes.size
119
+ assert_equal '200', res_codes[0]
120
+ end
121
+
122
+ test 'test incompatible message' do
123
+ conf = %(
124
+ proto_dir ./test/data/protos
125
+ tag test
126
+ in_mode binary
127
+ out_mode json
128
+ )
129
+ driver = create_driver(conf)
130
+ res_codes = []
131
+ driver.run do
132
+ path = '/test?msgtype=service.logging.Log'
133
+ res = post(path, @log_bin_batch)
134
+ res_codes << res.code
135
+ end
136
+ assert_equal 1, res_codes.size
137
+ assert_equal '400', res_codes[0]
138
+ end
11
139
  end
12
140
 
13
141
  private
14
142
 
15
- def create_driver(conf)
16
- Fluent::Test::Driver::Input.new(Fluent::Plugin::ProtobufHttpInput).configure(conf)
143
+ def post(path, body, type = :binary)
144
+ http = Net::HTTP.new('127.0.0.1', 8080)
145
+ content_type = 'application/octet-stream'
146
+ content_type = 'application/json' if type == :json
147
+ header = { 'Content-Type' => content_type }
148
+ req = Net::HTTP::Post.new(path, header)
149
+ req.body = body
150
+ http.request(req)
17
151
  end
18
152
  end
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-protobuf-http
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Azeem Sajid
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-11 00:00:00.000000000 Z
11
+ date: 2023-09-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.1.0
17
20
  - - "~>"
18
21
  - !ruby/object:Gem::Version
19
- version: '1.14'
22
+ version: '2.1'
20
23
  type: :development
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: 2.1.0
24
30
  - - "~>"
25
31
  - !ruby/object:Gem::Version
26
- version: '1.14'
32
+ version: '2.1'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: rake
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +44,26 @@ dependencies:
38
44
  - - "~>"
39
45
  - !ruby/object:Gem::Version
40
46
  version: '12.0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: simplecov
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.12'
54
+ - - "<="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.12.2
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.12'
64
+ - - "<="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.12.2
41
67
  - !ruby/object:Gem::Dependency
42
68
  name: test-unit
43
69
  requirement: !ruby/object:Gem::Requirement
@@ -100,13 +126,20 @@ executables: []
100
126
  extensions: []
101
127
  extra_rdoc_files: []
102
128
  files:
129
+ - ".github/dependabot.yml"
130
+ - ".github/workflows/ci.yml"
103
131
  - ".gitignore"
132
+ - ".rubocop.yml"
104
133
  - Gemfile
105
134
  - LICENSE
106
135
  - README.md
107
136
  - Rakefile
108
137
  - fluent-plugin-protobuf-http.gemspec
109
138
  - lib/fluent/plugin/in_protobuf_http.rb
139
+ - test/data/log.bin
140
+ - test/data/log.json
141
+ - test/data/logbatch5.bin
142
+ - test/data/protos/log.proto
110
143
  - test/helper.rb
111
144
  - test/plugin/test_in_protobuf_http.rb
112
145
  homepage: https://github.com/iamAzeem/fluent-plugin-protobuf-http
@@ -128,11 +161,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
128
161
  - !ruby/object:Gem::Version
129
162
  version: '0'
130
163
  requirements: []
131
- rubyforge_project:
132
- rubygems_version: 2.7.6
164
+ rubygems_version: 3.0.9
133
165
  signing_key:
134
166
  specification_version: 4
135
167
  summary: fluentd HTTP Input Plugin for Protocol Buffers
136
168
  test_files:
169
+ - test/data/log.bin
170
+ - test/data/log.json
171
+ - test/data/logbatch5.bin
172
+ - test/data/protos/log.proto
137
173
  - test/helper.rb
138
174
  - test/plugin/test_in_protobuf_http.rb