on_strum-healthcheck 0.1.0

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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +213 -0
  3. data/.circleci/gemspecs/compatible +25 -0
  4. data/.circleci/gemspecs/latest +33 -0
  5. data/.circleci/linter_configs/.bundler-audit.yml +4 -0
  6. data/.circleci/linter_configs/.commitspell.yml +30 -0
  7. data/.circleci/linter_configs/.cspell.yml +31 -0
  8. data/.circleci/linter_configs/.fasterer.yml +4 -0
  9. data/.circleci/linter_configs/.lefthook.yml +44 -0
  10. data/.circleci/linter_configs/.markdownlint.yml +9 -0
  11. data/.circleci/linter_configs/.rubocop.yml +137 -0
  12. data/.circleci/linter_configs/.yamllint.yml +7 -0
  13. data/.circleci/scripts/changeloglint.sh +22 -0
  14. data/.circleci/scripts/commitspell.sh +22 -0
  15. data/.circleci/scripts/release.sh +69 -0
  16. data/.circleci/scripts/set_publisher_credentials.sh +12 -0
  17. data/.codeclimate.yml +17 -0
  18. data/.github/BRANCH_NAMING_CONVENTION.md +36 -0
  19. data/.github/DEVELOPMENT_ENVIRONMENT_GUIDE.md +26 -0
  20. data/.github/ISSUE_TEMPLATE/bug_report.md +28 -0
  21. data/.github/ISSUE_TEMPLATE/feature_request.md +27 -0
  22. data/.github/ISSUE_TEMPLATE/issue_report.md +32 -0
  23. data/.github/ISSUE_TEMPLATE/question.md +22 -0
  24. data/.github/PULL_REQUEST_TEMPLATE.md +49 -0
  25. data/.gitignore +11 -0
  26. data/.reek.yml +39 -0
  27. data/.rspec +2 -0
  28. data/.ruby-gemset +1 -0
  29. data/.ruby-version +1 -0
  30. data/CHANGELOG.md +9 -0
  31. data/CODE_OF_CONDUCT.md +74 -0
  32. data/CONTRIBUTING.md +48 -0
  33. data/Gemfile +5 -0
  34. data/LICENSE.txt +21 -0
  35. data/README.md +224 -0
  36. data/Rakefile +8 -0
  37. data/bin/console +15 -0
  38. data/bin/setup +8 -0
  39. data/lib/on_strum/healthcheck/configuration.rb +141 -0
  40. data/lib/on_strum/healthcheck/core.rb +22 -0
  41. data/lib/on_strum/healthcheck/error/configuration/argument_type.rb +15 -0
  42. data/lib/on_strum/healthcheck/error/configuration/enpoint_pattern.rb +15 -0
  43. data/lib/on_strum/healthcheck/error/configuration/http_status_failure.rb +15 -0
  44. data/lib/on_strum/healthcheck/error/configuration/http_status_success.rb +15 -0
  45. data/lib/on_strum/healthcheck/error/configuration/not_callable_service.rb +15 -0
  46. data/lib/on_strum/healthcheck/error/configuration/not_configured.rb +15 -0
  47. data/lib/on_strum/healthcheck/error/configuration/unknown_service.rb +15 -0
  48. data/lib/on_strum/healthcheck/rack_middleware.rb +28 -0
  49. data/lib/on_strum/healthcheck/resolver.rb +85 -0
  50. data/lib/on_strum/healthcheck/version.rb +7 -0
  51. data/lib/on_strum/healthcheck.rb +25 -0
  52. data/on_strum-healthcheck.gemspec +28 -0
  53. metadata +164 -0
data/README.md ADDED
@@ -0,0 +1,224 @@
1
+ # ![OnStrum::Healthcheck - Simple configurable application healthcheck rack middleware](https://repository-images.githubusercontent.com/769335579/9094eefe-bfc8-483d-9a65-e406d1eb92c6)
2
+
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/b4dc21883d489d67fbef/maintainability)](https://codeclimate.com/github/on-strum/ruby-on-strum-healthcheck/maintainability)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/b4dc21883d489d67fbef/test_coverage)](https://codeclimate.com/github/on-strum/ruby-on-strum-healthcheck/test_coverage)
5
+ [![CircleCI](https://circleci.com/gh/on-strum/ruby-on-strum-healthcheck/tree/master.svg?style=svg)](https://circleci.com/gh/on-strum/ruby-on-strum-healthcheck/tree/master)
6
+ [![Gem Version](https://badge.fury.io/rb/ruby_healthcheck.svg)](https://badge.fury.io/rb/ruby_healthcheck)
7
+ [![Downloads](https://img.shields.io/gem/dt/ruby_healthcheck.svg?colorA=004d99&colorB=0073e6)](https://rubygems.org/gems/ruby_healthcheck)
8
+ [![GitHub](https://img.shields.io/github/license/on-strum/ruby-on-strum-healthcheck)](LICENSE.txt)
9
+ [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-v1.4%20adopted-ff69b4.svg)](CODE_OF_CONDUCT.md)
10
+
11
+ Simple configurable application healthcheck rack middleware. This middleware allows you to embed healthcheck endpoints into your rack based application to perform healthcheck probes. Make your application compatible with [Docker](https://docs.docker.com/reference/dockerfile/#healthcheck)/[Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-a-liveness-http-request) healthchecks in a seconds.
12
+
13
+ ## Table of Contents
14
+
15
+ - [Features](#features)
16
+ - [Requirements](#requirements)
17
+ - [Installation](#installation)
18
+ - [Configuring](#configuring)
19
+ - [Usage](#usage)
20
+ - [Rack](#rack)
21
+ - [Roda](#roda)
22
+ - [Hanami](#hanami)
23
+ - [Rails](#rails)
24
+ - [Contributing](#contributing)
25
+ - [License](#license)
26
+ - [Code of Conduct](#code-of-conduct)
27
+ - [Credits](#credits)
28
+ - [Versioning](#versioning)
29
+ - [Changelog](CHANGELOG.md)
30
+
31
+ ## Features
32
+
33
+ - Built-in default configuration
34
+ - Configurable services for startup/liveness/readiness probes
35
+ - Configurable root endpoints namespace
36
+ - Configurable startup/liveness/readiness probes endpoints
37
+ - Configurable successful/failure response statuses
38
+
39
+ ## Requirements
40
+
41
+ Ruby MRI 2.5.0+
42
+
43
+ ## Installation
44
+
45
+ Add this line to your application's Gemfile:
46
+
47
+ ```ruby
48
+ gem 'on_strum-healthcheck'
49
+ ```
50
+
51
+ And then execute:
52
+
53
+ ```bash
54
+ bundle
55
+ ```
56
+
57
+ Or install it yourself as:
58
+
59
+ ```bash
60
+ gem install on_strum-healthcheck
61
+ ```
62
+
63
+ ## Configuring
64
+
65
+ To start working with this gem, you must configure it first as in the example below:
66
+
67
+ ```ruby
68
+ # config/initializers/on_strum_healthcheck.rb
69
+
70
+ require 'on_strum/healthcheck'
71
+
72
+ OnStrum::Healthcheck.configure do |config|
73
+ # Optional parameter. The list of services that can be triggered
74
+ # during running probes. Each value of this hash should be callable
75
+ # and return boolean.
76
+ # It is equal to empty hash by default.
77
+ config.services = {
78
+ postges: -> { true },
79
+ redis: -> { true },
80
+ rabbit: -> { false }
81
+ }
82
+
83
+ # Optional parameter. The list of services that will be checked
84
+ # during running startup probe. As array items must be used an
85
+ # existing keys, defined in config.services.
86
+ # It is equal to empty array by default.
87
+ config.services_startup = %i[postges]
88
+
89
+ # Optional parameter. The list of services that will be checked
90
+ # during running liveness probe. As array items must be used an
91
+ # existing keys, defined in config.services.
92
+ # It is equal to empty array by default.
93
+ config.services_liveness = %i[redis]
94
+
95
+ # Optional parameter. The list of services that will be checked
96
+ # during running liveness probe. As array items must be used an
97
+ # existing keys, defined in config.services.
98
+ # It is equal to empty array by default.
99
+ config.services_readiness = %i[postges redis rabbit]
100
+
101
+ # Optional parameter. The name of middleware's root
102
+ # endpoints namespace. Use '/' if you want to use root
103
+ # namespace. It is equal to /healthcheck by default.
104
+ config.endpoints_namespace = '/application-healthcheck'
105
+
106
+ # Optional parameter. The startup endpoint path.
107
+ # It is equal to /startup by default.
108
+ config.endpoint_startup = '/startup-probe'
109
+
110
+ # Optional parameter. The liveness endpoint path.
111
+ # It is equal to /liveness by default.
112
+ config.endpoint_liveness = '/liveness-probe'
113
+
114
+ # Optional parameter. The readiness endpoint path.
115
+ # It is equal to /readiness by default.
116
+ config.endpoint_readiness = '/readiness-probe'
117
+
118
+ # Optional parameter. The HTTP successful status
119
+ # for startup probe. It is equal to 200 by default.
120
+ config.endpoint_startup_status_success = 201
121
+
122
+ # Optional parameter. The HTTP successful status
123
+ # for liveness probe. It is equal to 200 by default.
124
+ config.endpoint_liveness_status_success = 202
125
+
126
+ # Optional parameter. The HTTP successful status
127
+ # for readiness probe. It is equal to 200 by default.
128
+ config.endpoint_readiness_status_success = 203
129
+
130
+ # Optional parameter. The HTTP failure status
131
+ # for startup probe. It is equal to 500 by default.
132
+ config.endpoint_startup_status_failure = 501
133
+
134
+ # Optional parameter. The HTTP failure status
135
+ # for liveness probe. It is equal to 500 by default.
136
+ config.endpoint_liveness_status_failure = 502
137
+
138
+ # Optional parameter. The HTTP failure status
139
+ # for readiness probe. It is equal to 500 by default.
140
+ config.endpoint_readiness_status_failure = 503
141
+ end
142
+ ```
143
+
144
+ ## Usage
145
+
146
+ Please note, to start using this middleware you should configure `OnStrum::Healthcheck` before and then you should to add `OnStrum::Healthcheck::RackMiddleware` on the top of middlewares list.
147
+
148
+ ### Rack
149
+
150
+ ```ruby
151
+ require 'rack'
152
+ require 'on_strum/healthcheck'
153
+
154
+ OnStrum::Healthcheck.configure
155
+
156
+ RackCascade = Rack::Builder.app do
157
+ use OnStrum::Healthcheck::RackMiddleware
158
+ run YourApplication
159
+ end
160
+ ```
161
+
162
+ ### Roda
163
+
164
+ ```ruby
165
+ require 'roda'
166
+ require 'on_strum/healthcheck'
167
+
168
+ OnStrum::Healthcheck.configure
169
+
170
+ class YourApplication < Roda
171
+ use OnStrum::Healthcheck::RackMiddleware
172
+ end
173
+ ```
174
+
175
+ ### Hanami
176
+
177
+ ```ruby
178
+ # config/initializers/on_strum_healthcheck.rb
179
+
180
+ require 'on_strum/healthcheck'
181
+
182
+ OnStrum::Healthcheck.configure
183
+
184
+ # config/environment.rb
185
+
186
+ Hanami.configure do
187
+ middleware.use MyRackMiddleware
188
+ end
189
+ ```
190
+
191
+ ### Rails
192
+
193
+ ```ruby
194
+ # config/initializers/on_strum_healthcheck.rb
195
+
196
+ require 'on_strum/healthcheck'
197
+
198
+ OnStrum::Healthcheck.configure
199
+
200
+ # config/application.rb
201
+
202
+ config.middleware.use OnStrum::Healthcheck::RackMiddleware
203
+ ```
204
+
205
+ ## Contributing
206
+
207
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/on-strum/ruby-on-strum-healthcheck>. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. Please check the [open tickets](https://github.com/on-strum/ruby-on-strum-healthcheck/issues). Be sure to follow Contributor Code of Conduct below and our [Contributing Guidelines](CONTRIBUTING.md).
208
+
209
+ ## License
210
+
211
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
212
+
213
+ ## Code of Conduct
214
+
215
+ Everyone interacting in the `on_strum-healthcheck` project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
216
+
217
+ ## Credits
218
+
219
+ - [The Contributors](https://github.com/on-strum/ruby-on-strum-healthcheck/graphs/contributors) for code and awesome suggestions
220
+ - [The Stargazers](https://github.com/on-strum/ruby-on-strum-healthcheck/stargazers) for showing their support
221
+
222
+ ## Versioning
223
+
224
+ `on_strum-healthcheck` uses [Semantic Versioning 2.0.0](https://semver.org)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'on_strum/healthcheck'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,141 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ class Configuration
6
+ ATTRIBUTES = %i[
7
+ services
8
+ services_startup
9
+ services_liveness
10
+ services_readiness
11
+ endpoints_namespace
12
+ endpoint_startup
13
+ endpoint_liveness
14
+ endpoint_readiness
15
+ endpoint_startup_status_success
16
+ endpoint_liveness_status_success
17
+ endpoint_readiness_status_success
18
+ endpoint_startup_status_failure
19
+ endpoint_liveness_status_failure
20
+ endpoint_readiness_status_failure
21
+ ].freeze
22
+ ENDPOINTS_NAMESPACE = '/healthcheck'
23
+ ENDPOINT_STARTUP = '/startup'
24
+ ENDPOINT_LIVENESS = '/liveness'
25
+ ENDPOINT_READINESS = '/readiness'
26
+ DEFAULT_HTTP_STATUS_SUCCESS = 200
27
+ DEFAULT_HTTP_STATUS_FAILURE = 500
28
+ AVILABLE_HTTP_STATUSES_SUCCESS = (DEFAULT_HTTP_STATUS_SUCCESS..226).freeze
29
+ AVILABLE_HTTP_STATUSES_FAILURE = (DEFAULT_HTTP_STATUS_FAILURE..511).freeze
30
+
31
+ Settings = ::Struct.new(*OnStrum::Healthcheck::Configuration::ATTRIBUTES, keyword_init: true) do
32
+ def update(&block)
33
+ return self unless block
34
+
35
+ tap(&block)
36
+ end
37
+ end
38
+
39
+ attr_reader(*OnStrum::Healthcheck::Configuration::ATTRIBUTES)
40
+
41
+ def initialize(&block)
42
+ configuration_settings = build_configuration_settings(&block)
43
+ OnStrum::Healthcheck::Configuration::ATTRIBUTES.each do |attribute|
44
+ public_send(:"#{attribute}=", configuration_settings.public_send(attribute))
45
+ end
46
+ end
47
+
48
+ OnStrum::Healthcheck::Configuration::ATTRIBUTES.each do |attribute|
49
+ define_method(:"#{attribute}=") do |argument|
50
+ validate_attribute(__method__, attribute, argument)
51
+ instance_variable_set(:"@#{attribute}", argument)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def build_configuration_settings(&block) # rubocop:disable Metrics/MethodLength
58
+ OnStrum::Healthcheck::Configuration::Settings.new(
59
+ services: {},
60
+ services_startup: [],
61
+ services_liveness: [],
62
+ services_readiness: [],
63
+ endpoints_namespace: OnStrum::Healthcheck::Configuration::ENDPOINTS_NAMESPACE,
64
+ endpoint_startup: OnStrum::Healthcheck::Configuration::ENDPOINT_STARTUP,
65
+ endpoint_liveness: OnStrum::Healthcheck::Configuration::ENDPOINT_LIVENESS,
66
+ endpoint_readiness: OnStrum::Healthcheck::Configuration::ENDPOINT_READINESS,
67
+ endpoint_startup_status_success: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
68
+ endpoint_liveness_status_success: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
69
+ endpoint_readiness_status_success: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_SUCCESS,
70
+ endpoint_startup_status_failure: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_FAILURE,
71
+ endpoint_liveness_status_failure: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_FAILURE,
72
+ endpoint_readiness_status_failure: OnStrum::Healthcheck::Configuration::DEFAULT_HTTP_STATUS_FAILURE
73
+ ).update(&block)
74
+ end
75
+
76
+ def validate_attribute(method, attribute, value) # rubocop:disable Metrics/AbcSize
77
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::ArgumentType, method, *validator_argument_type(attribute, value))
78
+ case attribute
79
+ when OnStrum::Healthcheck::Configuration::ATTRIBUTES[0]
80
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::NotCallableService, method, *validator_services_callable(value))
81
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[1..3]
82
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::UnknownService, method, *validator_services_conformity(value))
83
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[4..7]
84
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::EnpointPattern, method, *validator_endpoint(value))
85
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[8..10]
86
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::HttpStatusSuccess, method, *validator_http_status_success(value))
87
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[11..13]
88
+ raise_unless(OnStrum::Healthcheck::Error::Configuration::HttpStatusFailure, method, *validator_http_status_failure(value))
89
+ end
90
+ end
91
+
92
+ def validator_argument_type(method_name, argument)
93
+ [
94
+ argument,
95
+ argument.is_a?(
96
+ case method_name
97
+ when :services then ::Hash
98
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[1..3] then ::Array
99
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[4..7] then ::String
100
+ when *OnStrum::Healthcheck::Configuration::ATTRIBUTES[8..13] then ::Integer
101
+ end
102
+ )
103
+ ]
104
+ end
105
+
106
+ def validator_endpoint(argument)
107
+ [argument, argument[%r{\A/.*\z}]]
108
+ end
109
+
110
+ def validator_http_status_success(argument)
111
+ [argument, OnStrum::Healthcheck::Configuration::AVILABLE_HTTP_STATUSES_SUCCESS.include?(argument)]
112
+ end
113
+
114
+ def validator_http_status_failure(argument)
115
+ [argument, OnStrum::Healthcheck::Configuration::AVILABLE_HTTP_STATUSES_FAILURE.include?(argument)]
116
+ end
117
+
118
+ def validator_services_callable(services_to_check, target_service = nil)
119
+ result = services_to_check.all? do |service_name, service_context|
120
+ target_service = service_name
121
+ service_context.respond_to?(:call)
122
+ end
123
+
124
+ [target_service, result]
125
+ end
126
+
127
+ def validator_services_conformity(services_to_check, target_service = nil)
128
+ result = services_to_check.all? do |service_name|
129
+ target_service = service_name
130
+ services.key?(service_name)
131
+ end
132
+
133
+ [target_service, result]
134
+ end
135
+
136
+ def raise_unless(exception_class, argument_name, argument_context, condition)
137
+ raise exception_class.new(argument_context, argument_name) unless condition
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ require_relative 'error/configuration/argument_type'
8
+ require_relative 'error/configuration/unknown_service'
9
+ require_relative 'error/configuration/not_callable_service'
10
+ require_relative 'error/configuration/enpoint_pattern'
11
+ require_relative 'error/configuration/http_status_success'
12
+ require_relative 'error/configuration/http_status_failure'
13
+ require_relative 'error/configuration/not_configured'
14
+ end
15
+ end
16
+
17
+ require_relative 'version'
18
+ require_relative 'configuration'
19
+ require_relative 'resolver'
20
+ require_relative 'rack_middleware'
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ ArgumentType = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("#{arg_value} is not a valid #{arg_name}")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ EnpointPattern = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("#{arg_value} does not match a valid enpoint pattern for #{arg_name}")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ HttpStatusFailure = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("Status #{arg_value} is wrong HTTP failure status for #{arg_name}. It should be in the range 500-511")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ HttpStatusSuccess = ::Class.new(::ArgumentError) do
8
+ def initialize(arg_value, arg_name)
9
+ super("Status #{arg_value} is wrong HTTP successful status for #{arg_name}. It should be in the range 200-226")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ NotCallableService = ::Class.new(::ArgumentError) do
8
+ def initialize(service_name, services_setter)
9
+ super("Service #{service_name} is not callable. All values for #{services_setter} should be a callable objects")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ NotConfigured = ::Class.new(::RuntimeError) do
8
+ def initialize
9
+ super('The configuration is empty. Please use OnStrum::Healthcheck.configure before')
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ module Error
6
+ module Configuration
7
+ UnknownService = ::Class.new(::ArgumentError) do
8
+ def initialize(service_name, services_setter)
9
+ super("Unknown #{service_name} service name for #{services_setter}. You should define it in config.services firstly")
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ class RackMiddleware
6
+ def initialize(
7
+ app,
8
+ resolver = OnStrum::Healthcheck::Resolver,
9
+ counfigured = !!OnStrum::Healthcheck.configuration,
10
+ *
11
+ )
12
+ @app = app
13
+ @resolver = resolver
14
+ @counfigured = counfigured
15
+ end
16
+
17
+ def call(env)
18
+ raise OnStrum::Healthcheck::Error::Configuration::NotConfigured unless counfigured
19
+
20
+ resolver.call(env) || app.call(env)
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :app, :resolver, :counfigured
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ class Resolver
6
+ require 'rack'
7
+ require 'securerandom'
8
+ require 'json'
9
+
10
+ PROBE_ENDPOINTS = %i[endpoint_startup endpoint_liveness endpoint_readiness].freeze
11
+ CONTENT_TYPE = { 'Content-Type' => 'application/json' }.freeze
12
+ JSONAPI_RESPONSE_TYPE = 'application-healthcheck'
13
+ ROOT_NAMESPACE = '/'
14
+
15
+ def self.call(rack_env)
16
+ new(rack_env).call
17
+ end
18
+
19
+ def initialize(rack_env)
20
+ @request = ::Rack::Request.new(rack_env)
21
+ end
22
+
23
+ def call
24
+ return unless probe_name
25
+
26
+ [response_status, OnStrum::Healthcheck::Resolver::CONTENT_TYPE, [response_jsonapi]]
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :request
32
+
33
+ def configuration
34
+ OnStrum::Healthcheck.configuration
35
+ end
36
+
37
+ def root_namespace
38
+ @root_namespace ||= configuration.endpoints_namespace
39
+ end
40
+
41
+ def root_namespace?
42
+ root_namespace.eql?(OnStrum::Healthcheck::Resolver::ROOT_NAMESPACE)
43
+ end
44
+
45
+ OnStrum::Healthcheck::Resolver::PROBE_ENDPOINTS.each do |method|
46
+ define_method(method) do
47
+ target_endpoint = configuration.public_send(method)
48
+ root_namespace? ? target_endpoint : "#{root_namespace}#{target_endpoint}"
49
+ end
50
+ end
51
+
52
+ def probe_name
53
+ @probe_name ||=
54
+ case request.path
55
+ when endpoint_startup then :startup
56
+ when endpoint_liveness then :liveness
57
+ when endpoint_readiness then :readiness
58
+ end
59
+ end
60
+
61
+ def probe_result
62
+ @probe_result ||=
63
+ configuration.public_send(:"services_#{probe_name}").each_with_object({}) do |service_name, response|
64
+ response[service_name] = configuration.services[service_name].call
65
+ end
66
+ end
67
+
68
+ def response_status
69
+ configuration.public_send(
70
+ probe_result.values.all? ? :"endpoint_#{probe_name}_status_success" : :"endpoint_#{probe_name}_status_failure"
71
+ )
72
+ end
73
+
74
+ def response_jsonapi
75
+ {
76
+ data: {
77
+ id: ::SecureRandom.uuid,
78
+ type: OnStrum::Healthcheck::Resolver::JSONAPI_RESPONSE_TYPE,
79
+ attributes: probe_result
80
+ }
81
+ }.to_json
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OnStrum
4
+ module Healthcheck
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'healthcheck/core'
4
+
5
+ module OnStrum
6
+ module Healthcheck
7
+ class << self
8
+ def configuration(&block)
9
+ @configuration ||= begin
10
+ return unless block
11
+
12
+ OnStrum::Healthcheck::Configuration.new(&block)
13
+ end
14
+ end
15
+
16
+ def configure(&block)
17
+ configuration(&block)
18
+ end
19
+
20
+ def reset_configuration!
21
+ @configuration = nil
22
+ end
23
+ end
24
+ end
25
+ end