fluent-plugin-containiq 0.0.7 → 0.0.8

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: 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: {}