rx-healthcheck 0.1.3 → 0.1.7

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: 547dbd721c4caff313db4e3c9e48669de9f036e83b78432e3b691eef100ca0a6
4
- data.tar.gz: dbed45238f4b0c4a9026ae1b0575ed00c904035640ed17b3b1c295fd5f56ab7f
3
+ metadata.gz: 83bc5957f3ce3f426b416b53e7d2e24782da340da998a030cd5f15c04d6b9dde
4
+ data.tar.gz: 5d6d75b3be95189399d743c234d0303d17183f43482a51ef20a1bed15d59e482
5
5
  SHA512:
6
- metadata.gz: 03fb63ab206a49a852b650bf8f0e80db431fb29ad4b778d804d6fe5f0a93abeae2a3ffe6985c5742818328ebae82ce7f5a23670781d5c695fb9794f8aee69586
7
- data.tar.gz: 05f26b6f89bbf8dcfde70ae563f7e7edaee52e72e57113872e4c6082fc952f791b7b9dbc26bdb7c7c28418fcb3973b8896f330d29ac034027832a386ad4f87a3
6
+ metadata.gz: 182ab4e67e807f373b4148f492e4cfc62d1f7af7176d187edc86a2282fd65813436b1fc65d71313c2bcebc6a559c2d28fe9140ffe4fb277922e2a4a610805dd1
7
+ data.tar.gz: e1e7bbeb1240b5994433b8e27775cb5ca5a9dab74fd3159b69ef1c5d93a76468a8c74068f65776b052b0ca1b219d1b4caf1bd50b1982f844f7b726101b0ed9e0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rx-healthcheck (0.1.1)
4
+ rx-healthcheck (0.1.4)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -59,14 +59,30 @@ deep_secondary: []
59
59
 
60
60
  Each collection must contain 0 or more `Rx::Check` objects. Those checks will be performed when the health check is queried. Deep checks will always also run the readiness checks.
61
61
 
62
+ ### Deep end-point authorization
63
+
64
+ It is considered as a good practice to protect the deep checks with a GUID to mitigate DDOS attacks. Hence you have 2 options to enable this. One is to use the `default_authorization` by passing the token to the authorization inside options hash, which you can use in a request with the authorization_token in the header of it. The other option would be to pass a lambda with an env argument (this gives you access to hash of request data) and have your own `custom_authorization`:
65
+
66
+ ```ruby
67
+ options: {
68
+ #default
69
+ authorization: <token>
70
+
71
+ #custom
72
+ authorization: -> (env) {
73
+ #your code goes here
74
+ }
75
+ }
76
+ ```
77
+
62
78
  ## Contributing
63
79
 
64
80
  Bug reports and pull requests are welcome on GitHub at https://github.com/zachpendleton/rx.
65
81
 
66
82
  Some tips for developing the gem locally:
67
83
 
68
- * Tests can be run by calling `rake`
69
- * You can point your Rails app to a local gem by adding a `path` option to your Gemfile, a la `gem "rx", path: "path/to/rx" (though you _will_ need to restart Rails whenever you change the gem).
84
+ - Tests can be run by calling `rake`
85
+ - You can point your Rails app to a local gem by adding a `path` option to your Gemfile, a la `gem "rx", path: "path/to/rx" (though you _will_ need to restart Rails whenever you change the gem).
70
86
 
71
87
  ## License
72
88
 
@@ -3,17 +3,20 @@ module Rx
3
3
  class FileSystemCheck
4
4
  FILENAME = "rx".freeze
5
5
 
6
- attr_reader :name
6
+ attr_reader :name, :timeout
7
7
 
8
- def initialize(name = "fs")
8
+ def initialize(name = "fs", timeout = 0)
9
9
  @name = name
10
+ @timeout = 0
10
11
  end
11
12
 
12
13
  def check
13
14
  Result.from(name) do
14
- !!Tempfile.open(FILENAME) do |f|
15
- f.write("ok")
16
- f.flush
15
+ Timeout::timeout(timeout) do
16
+ !!Tempfile.open(FILENAME) do |f|
17
+ f.write("ok")
18
+ f.flush
19
+ end
17
20
  end
18
21
  end
19
22
  end
@@ -1,16 +1,19 @@
1
+ require "timeout"
2
+
1
3
  module Rx
2
4
  module Check
3
5
  class GenericCheck
4
- attr_reader :name
6
+ attr_reader :name, :timeout
5
7
 
6
- def initialize(callable, name = "generic")
8
+ def initialize(callable, name = "generic", timeout = 0)
7
9
  @callable = callable
8
10
  @name = name
11
+ @timeout = timeout
9
12
  end
10
13
 
11
14
  def check
12
15
  Result.from(name) do
13
- callable.call
16
+ Timeout::timeout(timeout) { callable.call }
14
17
  end
15
18
  end
16
19
 
@@ -3,17 +3,18 @@ require "uri"
3
3
  module Rx
4
4
  module Check
5
5
  class HttpCheck
6
- attr_reader :name
6
+ attr_reader :name, :timeout
7
7
 
8
- def initialize(url, name = "http")
8
+ def initialize(url, name = "http", timeout: 1)
9
9
  @url = URI(url)
10
10
  @name = name
11
+ @timeout = timeout
11
12
  end
12
13
 
13
14
  def check
14
15
  Result.from(name) do
15
16
  http = Net::HTTP.new(url.host, url.port)
16
- http.read_timeout = 1
17
+ http.read_timeout = timeout
17
18
  http.use_ssl = url.scheme == "https"
18
19
 
19
20
  response = http.request(Net::HTTP::Get.new(path))
@@ -14,6 +14,10 @@ module Rx
14
14
  Future.new(&block).execute
15
15
  end
16
16
 
17
+ def self.thread_pool
18
+ @@pool
19
+ end
20
+
17
21
  def initialize(&block)
18
22
  @channel = Queue.new
19
23
  @state = :pending
@@ -6,6 +6,7 @@ module Rx
6
6
  def initialize(size = Etc.nprocessors)
7
7
  @pool = []
8
8
  @size = size
9
+ @pid = Process.pid
9
10
  end
10
11
 
11
12
  def shutdown
@@ -25,17 +26,33 @@ module Rx
25
26
  self
26
27
  end
27
28
 
29
+ def restart
30
+ shutdown
31
+ start
32
+ end
33
+
28
34
  def started?
29
35
  pool.map(&:alive?).any?
30
36
  end
31
37
 
32
38
  def submit(&block)
39
+ restart_on_fork if forked?
40
+
33
41
  return unless started?
34
42
  queue << block
35
43
  end
36
44
 
37
45
  private
38
- attr_reader :pool, :queue, :size
46
+ attr_reader :pid, :pool, :queue, :size
47
+
48
+ def forked?
49
+ Process.pid != pid
50
+ end
51
+
52
+ def restart_on_fork
53
+ restart
54
+ @pid = Process.pid
55
+ end
39
56
 
40
57
  def worker
41
58
  -> {
data/lib/rx/middleware.rb CHANGED
@@ -3,7 +3,8 @@ require "json"
3
3
  module Rx
4
4
  class Middleware
5
5
  DEFAULT_OPTIONS = {
6
- cache: true
6
+ cache: true,
7
+ authorization: nil
7
8
  }.freeze
8
9
 
9
10
  def initialize(app,
@@ -23,23 +24,27 @@ module Rx
23
24
  end
24
25
 
25
26
  def call(env)
26
- unless health_check_request?(env)
27
+ unless health_check_request?(path(env))
27
28
  return app.call(env)
28
29
  end
29
30
 
30
- case env["REQUEST_PATH"]
31
+ case path(env)
31
32
  when "/liveness"
32
33
  ok = check_to_component(liveness_checks).map { |x| x[:status] == 200 }.all?
33
34
  liveness_response(ok)
34
35
  when "/readiness"
35
36
  readiness_response(check_to_component(readiness_checks))
36
37
  when "/deep"
37
- @cache.cache("deep") do
38
- readiness = check_to_component(readiness_checks)
39
- critical = check_to_component(deep_critical_checks)
40
- secondary = check_to_component(deep_secondary_checks)
41
-
42
- deep_response(readiness, critical, secondary)
38
+ if !Rx::Util::HealthCheckAuthorization.new(env, @options[:authorization]).ok?
39
+ deep_response_authorization_failed
40
+ else
41
+ @cache.cache("deep") do
42
+ readiness = check_to_component(readiness_checks)
43
+ critical = check_to_component(deep_critical_checks)
44
+ secondary = check_to_component(deep_secondary_checks)
45
+
46
+ deep_response(readiness, critical, secondary)
47
+ end
43
48
  end
44
49
  end
45
50
  end
@@ -57,14 +62,18 @@ module Rx
57
62
  Rx::Cache::InMemoryCache.new
58
63
  end
59
64
 
60
- def health_check_request?(env)
61
- %w[/liveness /readiness /deep].include?(env["REQUEST_PATH"])
65
+ def health_check_request?(path)
66
+ %w[/liveness /readiness /deep].include?(path)
62
67
  end
63
68
 
64
69
  def liveness_response(is_ok)
65
70
  [is_ok ? 200 : 503, {}, []]
66
71
  end
67
72
 
73
+ def path(env)
74
+ env["PATH_INFO"] || env["REQUEST_PATH"] || env["REQUEST_URI"]
75
+ end
76
+
68
77
  def readiness_response(components)
69
78
  status = components.map { |x| x[:status] == 200 }.all? ? 200 : 503
70
79
 
@@ -75,6 +84,14 @@ module Rx
75
84
  ]
76
85
  end
77
86
 
87
+ def deep_response_authorization_failed
88
+ [
89
+ 403,
90
+ {"content-type" => "application/json"},
91
+ [JSON.dump({ message: "authorization failed" })]
92
+ ]
93
+ end
94
+
78
95
  def deep_response(readiness, critical, secondary)
79
96
  status = (readiness.map { |x| x[:status] == 200 } + critical.map { |x| x[:status] == 200 }).all? ? 200 : 503
80
97
 
@@ -0,0 +1,25 @@
1
+ module Rx
2
+ module Util
3
+ class HealthCheckAuthorization
4
+ HTTP_HEADER = "HTTP_AUTHORIZATION"
5
+
6
+ def initialize(env, authorization)
7
+ @authorization = authorization
8
+ @env = env
9
+ end
10
+
11
+ def ok?
12
+ case @authorization
13
+ when NilClass
14
+ true
15
+ when Proc
16
+ @authorization.call(@env)
17
+ when String
18
+ @authorization == @env[HTTP_HEADER]
19
+ else
20
+ raise StandardError.new("Authorization is not configured properly")
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
data/lib/rx/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Rx
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.7"
5
5
  end
data/lib/rx.rb CHANGED
@@ -12,6 +12,7 @@ require_relative "rx/check/result"
12
12
  require_relative "rx/concurrent/future"
13
13
  require_relative "rx/concurrent/thread_pool"
14
14
  require_relative "rx/util/heap"
15
+ require_relative "rx/util/health_check_authorization"
15
16
 
16
17
  module Rx
17
18
  class Error < StandardError; end
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rx-healthcheck
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Pendleton
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-26 00:00:00.000000000 Z
11
+ date: 2022-01-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description:
13
+ description:
14
14
  email:
15
15
  - zachpendleton@gmail.com
16
16
  executables: []
@@ -36,6 +36,7 @@ files:
36
36
  - lib/rx/concurrent/future.rb
37
37
  - lib/rx/concurrent/thread_pool.rb
38
38
  - lib/rx/middleware.rb
39
+ - lib/rx/util/health_check_authorization.rb
39
40
  - lib/rx/util/heap.rb
40
41
  - lib/rx/version.rb
41
42
  - rx.gemspec
@@ -45,7 +46,7 @@ licenses:
45
46
  metadata:
46
47
  homepage_uri: https://github.com/zachpendleton/rx
47
48
  source_code_uri: https://github.com/zachpendleton/rx
48
- post_install_message:
49
+ post_install_message:
49
50
  rdoc_options: []
50
51
  require_paths:
51
52
  - lib
@@ -60,8 +61,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
60
61
  - !ruby/object:Gem::Version
61
62
  version: '0'
62
63
  requirements: []
63
- rubygems_version: 3.1.2
64
- signing_key:
64
+ rubygems_version: 3.2.22
65
+ signing_key:
65
66
  specification_version: 4
66
67
  summary: Standard health checks for Rails and Rack applications
67
68
  test_files: []