sc4ry 0.1.8 → 0.2.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.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +27 -0
- data/.gitignore +1 -1
- data/Gemfile +4 -1
- data/README.md +23 -3
- data/Rakefile +31 -0
- data/VERSION +1 -0
- data/assets/images/sc4ry_workflow.png +0 -0
- data/lib/sc4ry/backends/memory.rb +35 -8
- data/lib/sc4ry/backends/redis.rb +21 -20
- data/lib/sc4ry/circuits.rb +178 -20
- data/lib/sc4ry/config.rb +36 -10
- data/lib/sc4ry/constants.rb +8 -0
- data/lib/sc4ry/dependencies.rb +6 -1
- data/lib/sc4ry/exceptions.rb +27 -2
- data/lib/sc4ry/helpers.rb +20 -6
- data/lib/sc4ry/logger.rb +45 -9
- data/lib/sc4ry/notifiers/init.rb +35 -11
- data/lib/sc4ry/notifiers/mattermost.rb +7 -2
- data/lib/sc4ry/notifiers/prometheus.rb +7 -1
- data/lib/sc4ry/run_controller.rb +22 -6
- data/lib/sc4ry/store.rb +74 -11
- data/lib/sc4ry/version.rb +7 -1
- data/sc4ry.gemspec +7 -3
- data/ultragreen_roodi_coding_convention.yml +25 -0
- metadata +90 -3
- data/assets/logo_sc4ry.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4cbfc8c2a7a821c3374916cdd69650a91f1eca5faa4dbbd93ac94e29dae5f89
|
4
|
+
data.tar.gz: dfa68d210342122e96e6f9fcd234fa73f6fefee667addac8371424b39c1b63d4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82590002c1ac457715776b0be304d77fa2903885cc4dc21ee45cea87ee070648661b48520453ab011cd287da49997990b4e607940a54954f5f82b5c884089715
|
7
|
+
data.tar.gz: 8b63d6b39cee270a1e1ab38ffcde491b746011126615bc4a5908b9f8790017f0c30c8899453ca1848ee4022f8528a0edd61587b583d58a8404e4eef139ecbb84
|
@@ -0,0 +1,27 @@
|
|
1
|
+
name: Ruby
|
2
|
+
|
3
|
+
on: [push,pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
container-job:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
container: ruby:latest
|
9
|
+
services:
|
10
|
+
redis:
|
11
|
+
image: redis
|
12
|
+
pushgateway:
|
13
|
+
image: prom/pushgateway
|
14
|
+
|
15
|
+
steps:
|
16
|
+
- uses: actions/checkout@v2
|
17
|
+
- name: Run the default task
|
18
|
+
run: |
|
19
|
+
gem install bundler -v 2.2.3
|
20
|
+
bundle install
|
21
|
+
bundle exec rake
|
22
|
+
env:
|
23
|
+
# The hostname used to communicate with the Redis service container
|
24
|
+
REDIS_HOST: redis
|
25
|
+
REDIS_PORT: 6379
|
26
|
+
PROM_PG_PORT: 9091
|
27
|
+
PROM_PG_HOST: pushgateway
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,7 +2,25 @@
|
|
2
2
|
|
3
3
|
Sc4ry provide the Circuit Breaker Design Pattern for your applications
|
4
4
|
|
5
|
-
](https://github.com/Ultragreen/sc4ry/actions?query=workflow%3ARuby+branch%3Amaster)
|
6
|
+

|
7
|
+
|
8
|
+
[](https://rubydoc.info/gems/sc4ry)
|
9
|
+

|
10
|
+

|
11
|
+

|
12
|
+

|
13
|
+
|
14
|
+

|
15
|
+
[](https://badge.fury.io/rb/sc4ry)
|
16
|
+

|
17
|
+

|
18
|
+

|
19
|
+
|
20
|
+
<noscript><a href="https://liberapay.com/ruydiaz/donate"><img alt="Donate using Liberapay" src="https://liberapay.com/assets/widgets/donate.svg"></a></noscript>
|
21
|
+
|
22
|
+

|
23
|
+
_Simple CircuitBreacker 4 RubY_
|
6
24
|
|
7
25
|
## Installation
|
8
26
|
|
@@ -22,6 +40,9 @@ Or install it yourself as:
|
|
22
40
|
|
23
41
|
## Usage
|
24
42
|
|
43
|
+
### Circuits States Worflow
|
44
|
+
|
45
|
+

|
25
46
|
### sample with Restclient
|
26
47
|
|
27
48
|
```ruby
|
@@ -109,8 +130,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
109
130
|
|
110
131
|
## Contributing
|
111
132
|
|
112
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
113
|
-
|
133
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/Ultragreen/sc4ry.
|
114
134
|
|
115
135
|
## License
|
116
136
|
|
data/Rakefile
CHANGED
@@ -1,6 +1,37 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
|
+
require 'yard'
|
4
|
+
require 'yard/rake/yardoc_task.rb'
|
5
|
+
require 'code_statistics'
|
6
|
+
require "roodi"
|
7
|
+
require "roodi_task"
|
8
|
+
require "version"
|
9
|
+
require 'rake/version_task'
|
10
|
+
Rake::VersionTask.new
|
11
|
+
|
12
|
+
|
13
|
+
RoodiTask.new() do | t |
|
14
|
+
t.patterns = %w(lib/**/*.rb)
|
15
|
+
t.config = "ultragreen_roodi_coding_convention.yml"
|
16
|
+
end
|
17
|
+
|
3
18
|
|
4
19
|
RSpec::Core::RakeTask.new(:spec)
|
5
20
|
|
6
21
|
task :default => :spec
|
22
|
+
|
23
|
+
|
24
|
+
YARD::Rake::YardocTask.new do |t|
|
25
|
+
t.files = [ 'lib/**/*.rb', '-', 'doc/**/*','spec/**/*_spec.rb']
|
26
|
+
t.options += ['-o', "yardoc"]
|
27
|
+
end
|
28
|
+
YARD::Config.load_plugin('yard-rspec')
|
29
|
+
|
30
|
+
namespace :yardoc do
|
31
|
+
task :clobber do
|
32
|
+
rm_r "yardoc" rescue nil
|
33
|
+
rm_r ".yardoc" rescue nil
|
34
|
+
rm_r "pkg" rescue nil
|
35
|
+
end
|
36
|
+
end
|
37
|
+
task :clobber => "yardoc:clobber"
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
Binary file
|
@@ -1,33 +1,60 @@
|
|
1
|
+
# Sc4ry module
|
2
|
+
# @note namespace
|
1
3
|
module Sc4ry
|
4
|
+
|
5
|
+
# Sc4ry:Backends module
|
6
|
+
# @note namespace
|
2
7
|
module Backends
|
3
8
|
|
9
|
+
# class of the memory backend
|
4
10
|
class Memory
|
11
|
+
|
12
|
+
# Constructor
|
13
|
+
# @param [Hash] config Config map
|
14
|
+
# @return [Sc4ry::Backends::Memory] a in Memory backend
|
5
15
|
def initialize(config=nil?)
|
6
16
|
@data = Hash::new
|
7
17
|
end
|
8
18
|
|
19
|
+
# return the list of find records in backend for a specific pattern
|
20
|
+
# @return [Array] list of record (for all hostname if hostname is specified)
|
9
21
|
def list
|
10
22
|
return @data.keys
|
11
23
|
end
|
12
24
|
|
13
|
-
|
14
|
-
|
25
|
+
# return value of queried record
|
26
|
+
# @param key [Symbol] the name of the record
|
27
|
+
# @return [String] content value of record
|
28
|
+
def get(key: )
|
29
|
+
return @data[key]
|
15
30
|
end
|
16
31
|
|
17
|
-
|
18
|
-
|
32
|
+
# defined and store value for specified key
|
33
|
+
# @param key [Symbol] :key the name of the record
|
34
|
+
# @param value [Symbol] :value the content value of the record
|
35
|
+
# @return [String] content value of record
|
36
|
+
def put(key:, value: )
|
37
|
+
@data[key] = value
|
19
38
|
end
|
20
39
|
|
21
|
-
|
22
|
-
|
40
|
+
# delete a specific record
|
41
|
+
# @param params [Symbol] the name of the record
|
42
|
+
# @return [Boolean] status of the operation
|
43
|
+
def del(key: )
|
44
|
+
@data.delete key
|
23
45
|
end
|
24
46
|
|
47
|
+
# flush all records in backend
|
48
|
+
# @return [Boolean] status of the operation
|
25
49
|
def flush
|
26
50
|
@data.clear
|
27
51
|
end
|
28
52
|
|
29
|
-
|
30
|
-
|
53
|
+
# verifiy a specific record existence
|
54
|
+
# @param key [Symbol] the name of the record
|
55
|
+
# @return [Boolean] presence of the record
|
56
|
+
def exist?(key: )
|
57
|
+
return @data.include? key
|
31
58
|
end
|
32
59
|
|
33
60
|
end
|
data/lib/sc4ry/backends/redis.rb
CHANGED
@@ -16,46 +16,47 @@ module Sc4ry
|
|
16
16
|
# return the list of find records in backend for a specific pattern
|
17
17
|
# @return [Array] list of record (for all hostname if hostname is specified)
|
18
18
|
def list
|
19
|
-
return @
|
19
|
+
return @be.keys('*').map(&:to_sym)
|
20
20
|
end
|
21
21
|
|
22
22
|
|
23
23
|
# return value of queried record
|
24
|
-
# @param [
|
25
|
-
# @option options [Symbol] :key the name of the record
|
24
|
+
# @param key [Symbol] the name of the record
|
26
25
|
# @return [String] content value of record
|
27
|
-
def get(
|
28
|
-
|
26
|
+
def get(key:)
|
27
|
+
res = YAML.load(@be.get(key))
|
28
|
+
res[:exceptions].map! {|item| item = Object.const_get(item) if item.class == String }
|
29
|
+
return res
|
29
30
|
end
|
30
31
|
|
31
32
|
# defined and store value for specified key
|
32
|
-
# @param [
|
33
|
-
# @
|
34
|
-
# @option options [Symbol] :value the content value of the record
|
33
|
+
# @param key [Symbol] :key the name of the record
|
34
|
+
# @param value [Symbol] :value the content value of the record
|
35
35
|
# @return [String] content value of record
|
36
|
-
def put(
|
37
|
-
|
36
|
+
def put(key: ,value:)
|
37
|
+
data = value.dup
|
38
|
+
data[:exceptions].map! {|item| item = item.name.to_s if item.class == Class }
|
39
|
+
@be.set key, data.to_yaml
|
38
40
|
end
|
39
41
|
|
40
42
|
# delete a specific record
|
41
|
-
# @param [
|
42
|
-
# @option options [Symbol] :key the name of the record
|
43
|
+
# @param key [Symbol] the name of the record
|
43
44
|
# @return [Boolean] status of the operation
|
44
|
-
def del(
|
45
|
-
@
|
45
|
+
def del(key: )
|
46
|
+
@be.del key
|
46
47
|
end
|
47
48
|
|
48
49
|
# flush all records in backend
|
50
|
+
# @return [Boolean] status of the operation
|
49
51
|
def flush
|
50
|
-
@
|
52
|
+
@be.flushdb
|
51
53
|
end
|
52
54
|
|
53
|
-
# verifiy a specific record
|
54
|
-
# @param [
|
55
|
-
# @option options [Symbol] :key the name of the record
|
55
|
+
# verifiy a specific record existence
|
56
|
+
# @param key [Symbol] the name of the record
|
56
57
|
# @return [Boolean] presence of the record
|
57
|
-
def exist?(
|
58
|
-
return ( not @
|
58
|
+
def exist?(key: )
|
59
|
+
return ( not @be.get(key).nil?)
|
59
60
|
end
|
60
61
|
|
61
62
|
|
data/lib/sc4ry/circuits.rb
CHANGED
@@ -1,17 +1,56 @@
|
|
1
|
+
# Sc4ry Module
|
2
|
+
# @note namespace
|
1
3
|
module Sc4ry
|
4
|
+
|
5
|
+
# Circuits and default configuration management class
|
2
6
|
class Circuits
|
3
7
|
|
4
8
|
include Sc4ry::Constants
|
5
9
|
include Sc4ry::Exceptions
|
6
10
|
|
7
11
|
@@circuits_store = Sc4ry::Store.instance
|
8
|
-
|
12
|
+
@@circuits_notifiers = Sc4ry::Notifiers
|
13
|
+
@@circuits_loggers = Sc4ry::Loggers
|
9
14
|
@@config = DEFAULT_CONFIG
|
10
15
|
|
16
|
+
# @!group forwarders
|
17
|
+
|
18
|
+
# Class method how forward the Notifiers class factory/manager
|
19
|
+
# @return [Sc4ry::Notifiers]
|
20
|
+
def Circuits.notifiers
|
21
|
+
return @@circuits_notifiers
|
22
|
+
end
|
23
|
+
|
24
|
+
# Class method how forward a Store manager class singleton
|
25
|
+
# @return [Sc4ry::Store]
|
26
|
+
def Circuits.store
|
27
|
+
return @@circuits_store
|
28
|
+
end
|
29
|
+
|
30
|
+
# Class method how forward the Logger manager class factory/manager
|
31
|
+
# @return [Sc4ry::Store]
|
32
|
+
def Circuits.loggers
|
33
|
+
return @@circuits_loggers
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
|
38
|
+
# @!endgroup
|
39
|
+
|
40
|
+
|
41
|
+
# @!group Default Sc4ry configuration management
|
42
|
+
|
43
|
+
# Class method how return de default Sc4ry config
|
44
|
+
# @return [Hash]
|
11
45
|
def Circuits.default_config
|
12
46
|
return @@config
|
13
47
|
end
|
14
48
|
|
49
|
+
# class method how merge a differential hash to default config
|
50
|
+
# @param [Hash] diff the differential hash config
|
51
|
+
# @example usage
|
52
|
+
# include Sc4ry
|
53
|
+
# Circuits.merge_default_config diff: {max_time: 20, notifiers: [:mattermost]}
|
15
54
|
def Circuits.merge_default_config(diff:)
|
16
55
|
validator = Sc4ry::Config::Validator::new(definition: diff, from: @@config)
|
17
56
|
validator.validate!
|
@@ -19,6 +58,13 @@ module Sc4ry
|
|
19
58
|
|
20
59
|
end
|
21
60
|
|
61
|
+
# class method for specifiying config by block
|
62
|
+
# @yield [Sc4ry::Config::ConfigMapper]
|
63
|
+
# @example usage
|
64
|
+
# include Sc4ry
|
65
|
+
# Circuits.configure do |spec|
|
66
|
+
# spec.max_failure_count = 3
|
67
|
+
# end
|
22
68
|
def Circuits.configure(&bloc)
|
23
69
|
mapper = Sc4ry::Config::ConfigMapper::new(definition: @@config.dup)
|
24
70
|
yield(mapper)
|
@@ -28,11 +74,33 @@ module Sc4ry
|
|
28
74
|
end
|
29
75
|
|
30
76
|
|
77
|
+
# old default config setter
|
78
|
+
# @deprecated use {.merge_default_config} instead
|
79
|
+
# @param [Hash] config
|
31
80
|
def Circuits.default_config=(config)
|
32
81
|
Sc4ry::Helpers.log level: :warn, message: "DEPRECATED: Circuits.default_config= is deprecated please use Circuits.merge_default_config add: {<config_hash>}"
|
33
82
|
Circuits.merge_default_config(diff: config)
|
34
83
|
end
|
35
84
|
|
85
|
+
# @!endgroup
|
86
|
+
|
87
|
+
# @!group Circuits management
|
88
|
+
|
89
|
+
# class method for registering a new circuit, cloud work with a block
|
90
|
+
# @yield [Sc4ry::Config::ConfigMapper]
|
91
|
+
# @param [Symbol] circuit a circuit name
|
92
|
+
# @param [Hash] config a config override on default config for the circuit
|
93
|
+
# @example usage
|
94
|
+
# include Sc4ry
|
95
|
+
# Circuits.register circuit: :mycircuit, config: {raise_on_opening: true, timeout: true}
|
96
|
+
# # or
|
97
|
+
# Circuits.register circuit: :mycircuit do |spec|
|
98
|
+
# spec.raise_on_opening = true
|
99
|
+
# spec.timeout = true
|
100
|
+
# end
|
101
|
+
# @return [Hash] the full config of the circuit after merge on default
|
102
|
+
# @raise [Sc4ryGenericError] if use config keyword with a block
|
103
|
+
# @raise [Sc4ryGenericError] if circuit already exist in current store.
|
36
104
|
def Circuits.register(circuit:, config: {})
|
37
105
|
if config.size > 0 and block_given? then
|
38
106
|
raise Sc4ryGenericError, "config: keyword must not be defined when block is given"
|
@@ -46,51 +114,141 @@ module Sc4ry
|
|
46
114
|
end
|
47
115
|
validator.validate!
|
48
116
|
Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit} : registered"
|
117
|
+
raise Sc4ryGenericError, "Circuit: #{circuit} already exist in store" if @@circuits_store.exist? key: circuit
|
49
118
|
@@circuits_store.put key: circuit, value: validator.result
|
119
|
+
return validator.result
|
50
120
|
end
|
51
121
|
|
122
|
+
# class method how list all circuits in current store
|
123
|
+
# @example usage
|
124
|
+
# include Sc4ry
|
125
|
+
# circuits = Circuits.list
|
126
|
+
# @return [Array] the list of [Symbol] circuits name
|
52
127
|
def Circuits.list
|
53
128
|
return @@circuits_store.list
|
54
129
|
end
|
55
130
|
|
131
|
+
# class method how flush all circuits in current store
|
132
|
+
# @example usage
|
133
|
+
# include Sc4ry
|
134
|
+
# Circuits.flush
|
135
|
+
# @return [true,false]
|
136
|
+
def Circuits.flush
|
137
|
+
return @@circuits_store.flush
|
138
|
+
end
|
56
139
|
|
57
|
-
|
58
|
-
|
140
|
+
# class method for unregistering a circuit
|
141
|
+
# @param [Symbol] circuit a circuit name
|
142
|
+
# @example usage
|
143
|
+
# include Sc4ry
|
144
|
+
# Circuits.unregister circuit: :mycircuit
|
145
|
+
# @raise [Sc4ryGenericError] if circuit not found in current store.
|
146
|
+
# @return [true,false]
|
147
|
+
def Circuits.unregister(circuit:)
|
148
|
+
if Circuits.list.include? circuit then
|
149
|
+
@@circuits_store.del key: circuit
|
150
|
+
return true
|
151
|
+
else
|
152
|
+
raise Sc4ryGenericError, "Circuit #{circuit} not found"
|
153
|
+
return false
|
154
|
+
end
|
59
155
|
end
|
60
156
|
|
61
|
-
|
157
|
+
|
158
|
+
# class method for get a specific circuit by circuit name
|
159
|
+
# @param [Symbol] circuit a circuit name
|
160
|
+
# @example usage
|
161
|
+
# include Sc4ry
|
162
|
+
# Circuits.get circuit: :mycircuit
|
163
|
+
# @return [Hash] the circuit record in current store included values and status if the circuit have already run.
|
164
|
+
def Circuits.get(circuit:)
|
165
|
+
@@circuits_store.get key: circuit
|
166
|
+
end
|
167
|
+
|
168
|
+
# class method for update the config of a specific circuit by circuit name
|
169
|
+
# @param [Symbol] circuit a circuit name
|
170
|
+
# @param [Hash] config a config hash to merge on current config
|
171
|
+
# @example usage
|
172
|
+
# include Sc4ry
|
173
|
+
# Circuits.update_config circuit: :mycircuit, config: {}
|
174
|
+
# @note : <b>important</b> updating config will reset status and values !
|
175
|
+
# @return [Hash] new config for this circuit
|
176
|
+
def Circuits.update_config(circuit: , config: {forward_unknown_exceptions: false})
|
177
|
+
raise Sc4ryGenericError, "Circuit #{circuit} not found" unless Circuits.list.include? circuit
|
178
|
+
save = @@circuits_store.get key: circuit
|
179
|
+
save.delete_if {|key,val| [:status,:values].include? key}
|
180
|
+
Circuits.unregister(circuit: circuit)
|
181
|
+
save.merge! config
|
182
|
+
return Circuits.register circuit: circuit, config: save
|
183
|
+
end
|
184
|
+
|
185
|
+
# class method for get the status of a specific circuit by circuit name
|
186
|
+
# @param [Symbol] circuit a circuit name
|
187
|
+
# @example usage
|
188
|
+
# include Sc4ry
|
189
|
+
# Circuits.status circuit: :mycircuit
|
190
|
+
# @return [Symbol] status must in [:open,:half_open,:closed,:never_run]
|
191
|
+
def Circuits.status(circuit:)
|
192
|
+
data = @@circuits_store.get key: circuit
|
193
|
+
return (data.include? :status)? data[:status][:general] : :never_run
|
194
|
+
end
|
195
|
+
|
196
|
+
|
197
|
+
# class method for running circuit, need a block
|
198
|
+
# @yield [Proc]
|
199
|
+
# @param [Symbol] circuit a circuit name
|
200
|
+
# @example usage
|
201
|
+
# include Sc4ry
|
202
|
+
# Circuits.run circuit: :mycircuit do
|
203
|
+
# # [...] your code like a Restclient.get("URL")
|
204
|
+
# end
|
205
|
+
# # or
|
206
|
+
# Circuits.run do
|
207
|
+
# # [...] your code like a Restclient.get("URL")
|
208
|
+
# # running with the first define circuit (use only on a one circuit usage)
|
209
|
+
# end
|
210
|
+
# @return [Hash] a result like ":general=>:open, :failure_count=>X, :overtime_count=>X, :timeout_count=>X"
|
211
|
+
# @raise [Sc4ryGenericError] if circuit already not exit, block is missing or store empty
|
212
|
+
def Circuits.run(circuit: nil , &block)
|
62
213
|
circuits_list = Circuits.list
|
63
214
|
raise Sc4ryGenericError, "No circuit block given" unless block_given?
|
64
215
|
raise Sc4ryGenericError, "No circuits defined" if circuits_list.empty?
|
65
|
-
circuit_name = (
|
216
|
+
circuit_name = (circuit)? circuit : circuits_list.first
|
66
217
|
raise Sc4ryGenericError, "Circuit #{circuit_name} not found" unless circuits_list.include? circuit_name
|
67
|
-
|
218
|
+
circuit_to_run = Circuits.get circuit: circuit_name
|
68
219
|
skip = false
|
69
|
-
if
|
70
|
-
if
|
220
|
+
if circuit_to_run.include? :status then
|
221
|
+
if circuit_to_run[:status][:general] == :open then
|
71
222
|
@now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
72
|
-
skip = true if ((@now -
|
223
|
+
skip = true if ((@now - circuit_to_run[:values].last[:end_time]) < circuit_to_run[:check_delay])
|
73
224
|
end
|
74
225
|
end
|
75
226
|
unless skip
|
76
|
-
controller = Sc4ry::RunController.new(
|
227
|
+
controller = Sc4ry::RunController.new(circuit_to_run)
|
77
228
|
Circuits.control circuit: circuit_name, values: controller.run(block: block)
|
78
229
|
end
|
79
|
-
|
80
|
-
|
230
|
+
result = @@circuits_store.get key: circuit_name
|
231
|
+
Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit_name} : status #{result[:status]}"
|
232
|
+
return result
|
81
233
|
end
|
82
234
|
|
235
|
+
# @!endgroup
|
236
|
+
|
83
237
|
private
|
84
|
-
|
85
|
-
|
238
|
+
# the private class method to control circuits running status
|
239
|
+
# @param [Symbol] circuit the name the circuit to control
|
240
|
+
# @param [Hash] values the resut value of a run
|
241
|
+
# @return [Boolean]
|
242
|
+
def Circuits.control(circuit: , values: )
|
243
|
+
data = @@circuits_store.get key: circuit
|
86
244
|
data[:status] = {:general => :closed, :failure_count => 0, :overtime_count => 0, :timeout_count => 0} unless data.include? :status
|
87
245
|
data[:values] = Array::new unless data.include? :values
|
88
246
|
level = [data[:max_failure_count].to_i, data[:max_timeout_count].to_i, data[:max_overtime_count].to_i].max
|
89
247
|
data[:values].shift if data[:values].size > level
|
90
|
-
data[:values].push
|
248
|
+
data[:values].push values
|
91
249
|
worst_status = []
|
92
250
|
['failure','overtime','timeout'].each do |control|
|
93
|
-
if
|
251
|
+
if values[control.to_sym] == true then
|
94
252
|
data[:status]["#{control}_count".to_sym] += 1
|
95
253
|
else
|
96
254
|
data[:status]["#{control}_count".to_sym] = 0
|
@@ -111,11 +269,11 @@ module Sc4ry
|
|
111
269
|
end
|
112
270
|
if save != data[:status][:general] then
|
113
271
|
raise CircuitBreaked if data[:status][:general] == :open and data[:raise_on_opening]
|
114
|
-
Sc4ry::Helpers.log level: :error, message: "Circuit #{
|
115
|
-
Sc4ry::Helpers.log level: :info, message: "Circuit #{
|
116
|
-
Sc4ry::Helpers.notify circuit:
|
272
|
+
Sc4ry::Helpers.log level: :error, message: "Circuit #{circuit} : breacking ! " if data[:status][:general] == :open
|
273
|
+
Sc4ry::Helpers.log level: :info, message: "Circuit #{circuit} : is now closed" if data[:status][:general] == :closed
|
274
|
+
Sc4ry::Helpers.notify circuit: circuit, config: data
|
117
275
|
end
|
118
|
-
@@circuits_store.put key:
|
276
|
+
@@circuits_store.put key: circuit, value: data
|
119
277
|
end
|
120
278
|
end
|
121
279
|
end
|
data/lib/sc4ry/config.rb
CHANGED
@@ -1,32 +1,49 @@
|
|
1
|
+
# Sc4ry module
|
2
|
+
# @note namespace
|
1
3
|
module Sc4ry
|
4
|
+
# Sc4ry::Config module
|
5
|
+
# @note namespace
|
2
6
|
module Config
|
7
|
+
# Configuration validator for Sc4ry default and circuits configuration
|
8
|
+
# @private
|
3
9
|
class Validator
|
4
|
-
|
5
|
-
|
10
|
+
|
11
|
+
# accessor on Circuit definition given for validation
|
12
|
+
attr_reader :input
|
13
|
+
# accessor on circuit initial definition from default config or given in construction by from keyword
|
14
|
+
# @note altered by reference to final result mapping with {#validate!}
|
15
|
+
attr_reader :config
|
6
16
|
|
7
17
|
def result
|
8
|
-
@
|
18
|
+
@config
|
9
19
|
end
|
10
20
|
|
11
21
|
include Sc4ry::Constants
|
12
22
|
include Sc4ry::Exceptions
|
13
|
-
|
23
|
+
# Validator constructor
|
24
|
+
# @param [Hash] definition the config hash to merge and validate
|
25
|
+
# @param [Hash] from config hash merged on origin (default : the Sc4ry base config from Constants )
|
26
|
+
# @return [Validator] a new instance of Validator
|
14
27
|
def initialize(definition: , from: DEFAULT_CONFIG)
|
15
|
-
@
|
16
|
-
@
|
28
|
+
@config = from
|
29
|
+
@input = definition
|
17
30
|
end
|
18
31
|
|
32
|
+
# Validation method, alter by reference the config attribut
|
33
|
+
# @raise ConfigError if un unknown key is given in definition to merge.
|
19
34
|
def validate!
|
20
|
-
unknown_keys = @
|
35
|
+
unknown_keys = @input.keys.difference @config.keys
|
21
36
|
raise ConfigError::new("Unknown keys in config set : #{unknown_keys.to_s}") unless unknown_keys.empty?
|
22
37
|
validate_formats
|
23
|
-
@
|
38
|
+
@config.merge! @input
|
24
39
|
format_exceptions
|
25
40
|
end
|
26
41
|
|
27
42
|
private
|
43
|
+
# Validation private sub method
|
44
|
+
# @raise ConfigError if proposed values haven't the good format and deeply in array
|
28
45
|
def validate_formats
|
29
|
-
|
46
|
+
@input.each do |spec,value|
|
30
47
|
raise ConfigError::new("#{spec} value #{DEFAULT_CONFIG_FORMATS[spec][:desc]}") unless DEFAULT_CONFIG_FORMATS[spec][:proc].call(value)
|
31
48
|
if DEFAULT_CONFIG_FORMATS[spec].include? :list then
|
32
49
|
value.each do |item|
|
@@ -36,17 +53,26 @@ module Sc4ry
|
|
36
53
|
end
|
37
54
|
end
|
38
55
|
|
56
|
+
# adapter for exception key in config String to Constant Class Name if need
|
57
|
+
# @note by reference
|
39
58
|
def format_exceptions
|
40
|
-
@
|
59
|
+
@config[:exceptions].map! {|item| item = (item.class == String)? Object.const_get(item) : item }
|
41
60
|
end
|
42
61
|
|
43
62
|
end
|
44
63
|
|
64
|
+
# Config Data mapper for block yielding methods for configuration
|
65
|
+
# @note work for/with {Sc4ry::Circuits.configure} and {Sc4ry::Circuits.register} when block given
|
45
66
|
class ConfigMapper
|
46
67
|
|
47
68
|
include Sc4ry::Constants
|
69
|
+
|
70
|
+
# config from given definition passed in constructor
|
48
71
|
attr_reader :config
|
49
72
|
|
73
|
+
# the mapping constructor from a given definition or the default From Sc4ry config (Constant)
|
74
|
+
# @param [Hash] definition a config hash
|
75
|
+
# @note creating dynamically accessors on config record given in definition
|
50
76
|
def initialize(definition: DEFAULT_CONFIG)
|
51
77
|
@config = definition
|
52
78
|
@config.each do |key,value|
|
data/lib/sc4ry/constants.rb
CHANGED
@@ -1,7 +1,14 @@
|
|
1
|
+
# Sc4ry module
|
2
|
+
# @note namespace
|
1
3
|
module Sc4ry
|
4
|
+
# Sc4ry::Constants module
|
5
|
+
# @note namespace
|
2
6
|
module Constants
|
3
7
|
|
8
|
+
# notifiers available in Sc4ry natively
|
4
9
|
CURRENT_NOTIFIERS = [:prometheus, :mattermost]
|
10
|
+
|
11
|
+
# the Sc4ry default config entries and values
|
5
12
|
DEFAULT_CONFIG = {
|
6
13
|
:max_failure_count => 5,
|
7
14
|
:timeout_value => 20,
|
@@ -16,6 +23,7 @@ module Sc4ry
|
|
16
23
|
:exceptions => [StandardError, RuntimeError]
|
17
24
|
}
|
18
25
|
|
26
|
+
# Default config supported entries with format and Proc checker for {Sc4ry::Config::Validator}
|
19
27
|
DEFAULT_CONFIG_FORMATS = {
|
20
28
|
:max_failure_count => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
|
21
29
|
:timeout_value => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
|