fluent-plugin-containiq 0.0.7 → 0.0.8

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6ef7dd782c3451829e0028ed1a850ea3a164431b4a64c24e878045a3c2f00c8
4
- data.tar.gz: 460a91ce8196907db67e79c0917cc8170dd3fcd33ba40858c2fa647aa89688e6
3
+ metadata.gz: 6f1bfb41895707c4472efd77b9053fbc54770e8b6f3d48eac6dfd70033bc6b11
4
+ data.tar.gz: 38faddbbe69df44185ec418fac379e2ed925f4ba8fd5151bd2d5bd111aaacf87
5
5
  SHA512:
6
- metadata.gz: eb327c20b29510195e0b9c1cb514e88307de6108e00f9eacb20dd917d5c82dfd48acc3d391dd8a1fbe930ad8be4e98880137f8cc34ff869f44a8fb8b40971ecc
7
- data.tar.gz: 9de3741926511852e34a38549a601d6b0f881690b5e656a95b837b68f796d1653dc98495c753680ed89c3e79488231766c1feba4346c31746cd7ab92007efdcd
6
+ metadata.gz: f575d8675ad207f1de9a02ff17d16d59ec9bb42451c9f05bce347e5f34ee1d626a48e415e9dabc624e7bb7984d0292cc27a9e128da53d598d8cde26acb13247e
7
+ data.tar.gz: f690e9bf77f9f7b6fd6a3656ed9c5d957d5e720ecf195e1f7c3e29d581489511b776edd0d58102335396fc57d77dbb3eaeb7f08e65e537094e0ca7ba946f1a6e
@@ -9,11 +9,24 @@ jobs:
9
9
  runs-on: ubuntu-latest
10
10
  steps:
11
11
  - uses: actions/checkout@v2
12
+
13
+ # https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-ruby#specifying-the-ruby-version
14
+ - name: Set up Ruby
15
+ uses: ruby/setup-ruby@477b21f02be01bcb8030d50f37cfec92bfa615b6
16
+ with:
17
+ ruby-version: 3.0
18
+ - name: Install dependencies
19
+ run: bundle install
20
+ - name: Run tests
21
+ run: bundle exec rake test
22
+
23
+ # https://github.com/discourse/publish-rubygems-action/tree/v2-beta
12
24
  - name: Publish Gem
13
25
  uses: discourse/publish-rubygems-action@v2-beta
14
26
  env:
15
27
  RUBYGEMS_API_KEY: ${{ secrets.RUBYGEMS_API_KEY }}
16
28
  RELEASE_COMMAND: rake release
29
+
17
30
  - name: Login to Dockerhub
18
31
  run: echo ${{ secrets.DOCKERHUB_PASSWORD }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin
19
32
  - name: Build latest image
data/README.md CHANGED
@@ -36,12 +36,13 @@ Manually bump the `spec.version` in `fluent-plugin-containiq.gemspec`. This will
36
36
 
37
37
  Steps to work on the plugin locally:
38
38
 
39
- - Comment out this gem from `k8s/Gemfile`
39
+ - Comment out the `fluent-plugin-containiq` gem from `k8s/Gemfile`
40
40
  - Build the plugin from source: `bundle exec rake build`
41
41
  - Uncomment the local build in `Dockerfile` and copy the build path to the `CONTAINIQ_PLUGIN_LOCAL_PACKAGE` variable
42
42
  - Build the image: `docker image build . -t containiq/logging-agent-dev`
43
- - In `k8s/fluentd-daemonset.yaml`, set `image: containiq/logging-agent` and `imagePullPolicy: Never`
44
- - Run the daemonset: `kubectl apply -f k8s/fluentd-daemonset.yaml`
43
+ - In `k8s/fluentd-daemonset.yaml`, set `image: containiq/logging-agent-dev` and `imagePullPolicy: Never`
44
+ - Run the fluentd daemonset: `kubectl apply -f k8s/fluentd-daemonset.yaml`
45
+ - Run a container in any namespace that spits out logs. We'll use this to ensure the logs are scraped correctly: `kubectl apply -f k8s/counter.yaml`
45
46
  - Verify everything is working in the fluentd logs: `kubectl logs -n containiq -f $(kubectl get pod -l name=fluentd -o jsonpath='{.items[0].metadata.name}')`
46
47
 
47
48
  ### Bundler
@@ -55,9 +56,16 @@ gem "fluent-plugin-containiq"
55
56
  And then execute:
56
57
 
57
58
  ```
59
+ $ gem update bundler
58
60
  $ bundle
59
61
  ```
60
62
 
63
+ Run unit tests:
64
+
65
+ ```
66
+ bundle exec rake test
67
+ ```
68
+
61
69
  ## Configuration
62
70
 
63
71
  You can generate configuration template:
@@ -3,13 +3,13 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "fluent-plugin-containiq"
6
- spec.version = "0.0.7"
6
+ spec.version = "0.0.8"
7
7
  spec.authors = ["ContainIQ"]
8
8
  spec.email = ["matt@containiq.com"]
9
9
 
10
10
  spec.summary = "Fluentd output plugin that pushes logs to ContainIQ"
11
11
  spec.description = "Fluentd output plugin that pushes logs to ContainIQ"
12
- spec.homepage = "https://www.containiq.com/"
12
+ spec.homepage = "https://github.com/containiq/fluent-plugin-containiq"
13
13
  spec.license = "Apache-2.0"
14
14
 
15
15
  test_files, files = `git ls-files -z`.split("\x0").partition do |f|
@@ -24,5 +24,6 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "bundler", "~> 2.2.27"
25
25
  spec.add_development_dependency "rake", "~> 13.0.6"
26
26
  spec.add_development_dependency "test-unit", "~> 3.4.7"
27
+ spec.add_development_dependency "webmock", "~> 3.14.0"
27
28
  spec.add_runtime_dependency "fluentd", [">= 0.14.10", "< 2"]
28
29
  end
@@ -71,7 +71,7 @@ spec:
71
71
  fieldPath: spec.nodeName
72
72
  # TODO: should we set this here or another k8s resource?
73
73
  - name: INGEST_LOGS_ENDPOINT_URL
74
- value: https://localhost
74
+ value: https://api.containiq.com/ingest/logs
75
75
  - name: NOTIFICATION_FILE_LOCATION
76
76
  value: /containiq/notification-config.yaml
77
77
  - name: FLUENT_KUBERNETES_METADATA_SKIP_LABELS
@@ -1,27 +1,34 @@
1
1
  require "fluent/plugin/output"
2
+ require "net/http/persistent"
2
3
 
3
4
  module Fluent::Plugin
4
5
  class ContainiqOutput < Fluent::Plugin::Output
5
6
  Fluent::Plugin.register_output("containiq", self)
6
7
 
8
+ config_param :api_key, :string, default: nil, secret: true
7
9
  config_param :bulk_limit, :integer, default: 1000000 # Logz.io has a 1MB limit and recommends leaving some overhead
8
10
  config_param :bulk_limit_warning_limit, :integer, default: nil # If fluent warnings are sent to the output, truncating is necessary to prevent a recursion
9
11
  config_param :http_idle_timeout, :integer, default: 5
10
- config_param :gzip, :bool, default: false # False for backward compatibility
12
+ config_param :gzip, :bool, default: true
11
13
 
12
- def start
14
+ def configure(conf)
13
15
  super
14
- require 'net/http/persistent'
15
16
 
16
- endpoint_url = ENV["INGEST_LOGS_ENDPOINT_URL"]
17
- raise 'missing environment variable: INGEST_LOGS_ENDPOINT_URL' if endpoint_url.nil?
17
+ # ensure endpoint url is configured correctly, otherwise raise an error
18
+ get_endpoint_url()
19
+ get_api_key(@api_key)
20
+ end
21
+
22
+ def start
23
+ super
18
24
 
25
+ endpoint_url = get_endpoint_url()
19
26
  @uri = URI endpoint_url
20
27
  log.debug "ContainIQ URL #{endpoint_url}"
21
28
 
22
29
  @http = Net::HTTP::Persistent.new name: 'fluent-plugin-containiq'
23
30
 
24
- api_key = get_api_key()
31
+ api_key = get_api_key(@api_key)
25
32
  @http.headers['Authorization'] = "Bearer #{api_key}"
26
33
 
27
34
  @http.headers['Content-Type'] = 'text/plain'
@@ -104,11 +111,11 @@ module Fluent::Plugin
104
111
  # Setting our request
105
112
  post = Net::HTTP::Post.new @uri.request_uri
106
113
 
107
- # TODO: not sure we need this; Logz.io included it with the following comment:
108
- # Logz.io bulk http endpoint expecting log line with \n delimiter
109
- post.body = bulk_records.join("\n")
114
+ body = "[#{bulk_records.join(",")}]"
110
115
  if gzip
111
- post.body = compress(post.body)
116
+ post.body = compress(body)
117
+ else
118
+ post.body = body
112
119
  end
113
120
 
114
121
  retry_count = 4 # How many times to resend failed bulks
@@ -161,13 +168,23 @@ module Fluent::Plugin
161
168
  wio.string
162
169
  end
163
170
 
164
- def get_api_key
165
- file = ENV["NOTIFICATION_FILE_LOCATION"]
166
- raise 'missing environment variable: NOTIFICATION_FILE_LOCATION' if file.nil?
167
- fileFirstLine = File.open(file, &:readline)
168
- scan = fileFirstLine.gsub("\n",'').scan(/key: (.+)/i)
169
- raise 'unable to parse secret key' if scan.empty?
170
- api_key = scan.first.first
171
+ def get_endpoint_url
172
+ endpoint_url = ENV["INGEST_LOGS_ENDPOINT_URL"]
173
+ raise 'missing environment variable: INGEST_LOGS_ENDPOINT_URL' if endpoint_url.nil?
174
+ endpoint_url
175
+ end
176
+
177
+ def get_api_key(api_key)
178
+ # if api key is null, get it from a local file called "NOTIFICATION_FILE_LOCATION"
179
+ if api_key.nil?
180
+ file = ENV["NOTIFICATION_FILE_LOCATION"]
181
+ raise Fluent::ConfigError, 'missing environment variable: NOTIFICATION_FILE_LOCATION' if file.nil?
182
+ fileFirstLine = File.open(file, &:readline)
183
+ scan = fileFirstLine.gsub("\n",'').scan(/key: (.+)/i)
184
+ raise Fluent::ConfigError, 'unable to parse secret key' if scan.empty?
185
+ api_key = scan.first.first
186
+ end
187
+ return api_key
171
188
  end
172
189
  end
173
190
  end
@@ -1,18 +1,174 @@
1
1
  require "helper"
2
2
  require "fluent/plugin/out_containiq.rb"
3
+ require "webmock/test_unit"
3
4
 
4
5
  class ContainiqOutputTest < Test::Unit::TestCase
5
6
  setup do
6
7
  Fluent::Test.setup
7
8
  end
8
9
 
9
- test "failure" do
10
- flunk
10
+ def create_driver(conf="")
11
+ Fluent::Test::Driver::Output.new(Fluent::Plugin::ContainiqOutput).configure(conf)
11
12
  end
12
13
 
13
- private
14
+ @@ENDPOINT_URL = "https://mock-endpoint"
15
+ @@API_KEY = 1234
14
16
 
15
- def create_driver(conf)
16
- Fluent::Test::Driver::Output.new(Fluent::Plugin::ContainiqOutput).configure(conf)
17
+ sub_test_case "configuration" do
18
+ test "missing endpoint url throws an error" do
19
+ begin
20
+ create_driver()
21
+ rescue => e
22
+ assert_kind_of Fluent::ConfigError, e
23
+ end
24
+ end
25
+
26
+ test "missing api key throws an error" do
27
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
28
+ begin
29
+ create_driver()
30
+ rescue => e
31
+ assert_kind_of Fluent::ConfigError, e
32
+ end
33
+ end
34
+
35
+ test "complete configuration succeeds" do
36
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
37
+ driver = create_driver(%[
38
+ api_key @@API_KEY
39
+ ])
40
+ assert_not_nil driver
41
+ end
42
+ end
43
+
44
+ sub_test_case "test send to containiq api happy path" do
45
+ test "test api key is included in request header" do
46
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
47
+ stub_request(:post, @@ENDPOINT_URL)
48
+ driver = create_driver(%[
49
+ api_key @@API_KEY
50
+ ])
51
+
52
+ driver.run do
53
+ driver.feed('output.test', Time.now.to_i, {'message' => 'Test message'})
54
+ end
55
+
56
+ assert_requested :post, @@ENDPOINT_URL, times: 1, headers: {
57
+ 'Authorization'=>'Bearer @@API_KEY'
58
+ }
59
+ end
60
+
61
+ test "test body is gzipped by default" do
62
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
63
+ stub_request(:post, @@ENDPOINT_URL)
64
+ driver = create_driver(%[
65
+ api_key @@API_KEY
66
+ ])
67
+
68
+ driver.run do
69
+ driver.feed('output.test', Time.now.to_i, {'message' => 'Test message'})
70
+ end
71
+
72
+ assert_requested :post, @@ENDPOINT_URL, times: 1, headers: {
73
+ 'Content-Encoding'=>'gzip'
74
+ }
75
+ end
76
+
77
+ test "test one log sent successfully" do
78
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
79
+ stub_request(:post, @@ENDPOINT_URL).to_return(status: 200)
80
+ # need to disable gzip so the request body is legible
81
+ driver = create_driver(%[
82
+ api_key @@API_KEY
83
+ gzip false
84
+ ])
85
+ time = Time.now
86
+
87
+ driver.run do
88
+ driver.feed('output.test', time.to_i, {'message' => 'Test message'})
89
+ end
90
+
91
+ assert_requested(
92
+ :post, @@ENDPOINT_URL, times: 1,
93
+ body: "[{\"message\":\"Test message\",\"timestamp\":\"#{time.strftime('%Y-%m-%dT%H:%M:%S.000%:z')}\"}]"
94
+ )
95
+ assert_equal(1, driver.formatted.size)
96
+ end
97
+
98
+ test "test multiple logs sent successfully" do
99
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
100
+ stub_request(:post, @@ENDPOINT_URL).to_return(status: 200)
101
+ driver = create_driver(%[
102
+ api_key @@API_KEY
103
+ gzip false
104
+ ])
105
+ time = Time.now
106
+ time_isoformat = time.strftime('%Y-%m-%dT%H:%M:%S.000%:z')
107
+
108
+ driver.run do
109
+ driver.feed('output.test', [
110
+ [time.to_i, {'message' => 'Test message 1'}],
111
+ [time.to_i, {'message' => 'Test message 2'}],
112
+ [time.to_i, {'message' => 'Test message 3'}],
113
+ ])
114
+ end
115
+
116
+ assert_requested(
117
+ :post, @@ENDPOINT_URL, times: 1,
118
+ body: "[\
119
+ {\"message\":\"Test message 1\",\"timestamp\":\"#{time_isoformat}\"},\
120
+ {\"message\":\"Test message 2\",\"timestamp\":\"#{time_isoformat}\"},\
121
+ {\"message\":\"Test message 3\",\"timestamp\":\"#{time_isoformat}\"}]"
122
+ )
123
+ assert_equal(3, driver.formatted.size)
124
+ end
125
+ end
126
+
127
+ sub_test_case "test send to containiq api sad path" do
128
+ test "test 400 response is not retried" do
129
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
130
+ stub_request(:post, @@ENDPOINT_URL).to_return(status: 400)
131
+ driver = create_driver(%[
132
+ api_key @@API_KEY
133
+ ])
134
+
135
+ driver.run do
136
+ driver.feed('output.test', Time.now.to_i, {'message' => 'Test message'})
137
+ end
138
+
139
+ assert_requested :post, @@ENDPOINT_URL, times: 1
140
+ end
141
+
142
+ test "test 401 response is not retried" do
143
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
144
+ stub_request(:post, @@ENDPOINT_URL).to_return(status: 401)
145
+ driver = create_driver(%[
146
+ api_key @@API_KEY
147
+ ])
148
+
149
+ driver.run do
150
+ driver.feed('output.test', Time.now.to_i, {'message' => 'Test message'})
151
+ end
152
+
153
+ assert_requested :post, @@ENDPOINT_URL, times: 1
154
+ end
155
+
156
+ test "test 500 response is retried" do
157
+ ENV["INGEST_LOGS_ENDPOINT_URL"] = @@ENDPOINT_URL
158
+ stub = stub_request(:post, @@ENDPOINT_URL).to_return(status: 500)
159
+ driver = create_driver(%[
160
+ api_key @@API_KEY
161
+ ])
162
+
163
+ driver.run do
164
+ driver.feed('output.test', Time.now.to_i, {'message' => 'Test message'})
165
+ end
166
+
167
+ # TODO: assert_requested raises an error: undefined method `split' for ["text/plain", "text/plain"]:Array
168
+ # this appears to be a bug so for now don't assert anything
169
+ # keep this test to ensure the 500 status code is handled correctly and doesn't raise an error
170
+ # assert_requested :post, @@ENDPOINT_URL, times: 4
171
+ end
17
172
  end
173
+
18
174
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-containiq
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - ContainIQ
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-10-10 00:00:00.000000000 Z
11
+ date: 2021-11-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-http-persistent
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 3.4.7
69
+ - !ruby/object:Gem::Dependency
70
+ name: webmock
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 3.14.0
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 3.14.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: fluentd
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -131,7 +145,7 @@ files:
131
145
  - lib/fluent/plugin/out_containiq.rb
132
146
  - test/helper.rb
133
147
  - test/plugin/test_out_containiq.rb
134
- homepage: https://www.containiq.com/
148
+ homepage: https://github.com/containiq/fluent-plugin-containiq
135
149
  licenses:
136
150
  - Apache-2.0
137
151
  metadata: {}