sc4ry 0.2.0 → 0.2.1

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: f4cbfc8c2a7a821c3374916cdd69650a91f1eca5faa4dbbd93ac94e29dae5f89
4
- data.tar.gz: dfa68d210342122e96e6f9fcd234fa73f6fefee667addac8371424b39c1b63d4
3
+ metadata.gz: 5a0a0b0ccc0176505d96bfe85b978f155a989db942cc031972d7c31f24db0d2a
4
+ data.tar.gz: 1e0a528951e291efb0f04b05eaa6ae0897a6a98fb3834b11e7a8e45597848da4
5
5
  SHA512:
6
- metadata.gz: 82590002c1ac457715776b0be304d77fa2903885cc4dc21ee45cea87ee070648661b48520453ab011cd287da49997990b4e607940a54954f5f82b5c884089715
7
- data.tar.gz: 8b63d6b39cee270a1e1ab38ffcde491b746011126615bc4a5908b9f8790017f0c30c8899453ca1848ee4022f8528a0edd61587b583d58a8404e4eef139ecbb84
6
+ metadata.gz: c0159b1b8254dbc358eb68b33cb96cafdd5007c065e549ce307fbeacfeaf22f4739181b996fb62ce27dba3b33947eccd6e1d497970c0c218d8ec199633d3f480
7
+ data.tar.gz: 58280b4b7fe4cc20c30738aabaf258e35efc349dc0df1d2211a058e3b9acd7d61f55df9b3a9cda01b5493c9cfd48bd96993835460c5da8e4205f8a85f74e9559
data/.rubocop.yml ADDED
@@ -0,0 +1,47 @@
1
+ # This configuration was generated by
2
+ # `rubocop --auto-gen-config`
3
+ # on 2022-07-26 15:23:44 UTC using RuboCop version 1.32.0.
4
+ # The point is for the user to remove these configuration records
5
+ # one by one as the offenses are removed from the code base.
6
+ # Note that changes in the inspected code, or installation of new
7
+ # versions of RuboCop, may require this file to be generated again.
8
+
9
+ AllCops:
10
+ NewCops: enable
11
+ SuggestExtensions: false
12
+ Exclude:
13
+ - 'spec/sc4ry_spec.rb'
14
+ - 'samples/test.rb'
15
+
16
+
17
+ # definitive :
18
+ Style/ClassVars:
19
+ Enabled: false
20
+ Gemspec/RequireMFA:
21
+ Enabled: false
22
+ Security/YAMLLoad:
23
+ Enabled: false
24
+ Style/MutableConstant:
25
+ Enabled: false
26
+
27
+ # to study :
28
+
29
+ Metrics/ClassLength:
30
+ Enabled: false
31
+ Metrics/AbcSize:
32
+ Enabled: false
33
+ Metrics/CyclomaticComplexity:
34
+ Enabled: false
35
+ Metrics/MethodLength:
36
+ Enabled: false
37
+ Metrics/PerceivedComplexity:
38
+ Enabled: false
39
+
40
+
41
+
42
+
43
+
44
+
45
+
46
+
47
+
data/Gemfile CHANGED
@@ -1,12 +1,13 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in sc4ry.gemspec
4
6
  gemspec
5
7
 
6
- gem "rake", "~> 12.0"
7
- gem "rspec", "~> 3.0"
8
- gem "prometheus-client", "~> 3.0"
9
- gem "rest-client", "~> 2.1"
10
- gem "redis", "~> 4.6"
11
- gem "version", "~> 1.1"
12
-
8
+ gem 'prometheus-client', '~> 3.0'
9
+ gem 'rake', '~> 12.0'
10
+ gem 'redis', '~> 4.6'
11
+ gem 'rest-client', '~> 2.1'
12
+ gem 'rspec', '~> 3.0'
13
+ gem 'version', '~> 1.1'
data/README.md CHANGED
@@ -43,83 +43,282 @@ Or install it yourself as:
43
43
  ### Circuits States Worflow
44
44
 
45
45
  ![Sc4ry workflow](assets/images/sc4ry_workflow.png)
46
- ### sample with Restclient
46
+ ### Quickstart : sample with Restclient
47
+
48
+ A quick circuit test :
49
+ create a test script lile test.rb :
47
50
 
48
51
  ```ruby
49
52
 
50
- require 'rubygems'
51
- require 'sc4ry'
53
+ require 'rubygems'
54
+ require 'sc4ry'
52
55
 
56
+ include Sc4ry
53
57
 
54
- puts 'Initial default config'
55
- pp Sc4ry::Circuits.default_config
58
+ Circuits.register circuit: :mycircuit, config: {:notifiers => [:prometheus,:mattermost],
59
+ :exceptions => [Errno::ECONNREFUSED, URI::InvalidURIError]}
56
60
 
61
+ Circuits.run do # circuit: :mycircuit is optional, run work with the first circuit registered
62
+ puts RestClient.get('http://your_service/endpoint')
63
+ end
57
64
 
58
- Sc4ry::Circuits.merge_default_config diff: {timeout: true }
59
- # or with a block
60
- Sc4ry::Circuits.configure do |spec|
61
- spec.max_time = 12
62
- end
65
+
63
66
 
67
+ ```
64
68
 
65
- # display default config
66
- puts 'Default config'
67
- pp Sc4ry::Circuits.default_config
69
+ ### the Sc4ry Config default values :
68
70
 
71
+ Default values, circuit is half open before one of the max count is reached :
69
72
 
70
- # defining a circuit, config must be empty or override from default
71
- Sc4ry::Circuits.register circuit: :test, config: {:notifiers => [:prometheus,:mattermost], :exceptions => [Errno::ECONNREFUSED, URI::InvalidURIError] }
72
- # or with a block
73
- Sc4ry::Circuits.register circuit: :test2 do |spec|
74
- spec.exceptions = [Errno::ECONNREFUSED]
75
- end
76
- # or
77
- Sc4ry::Circuits.register circuit: :test3
73
+ * _:max_failure_count_
74
+ <br>*description* : maximum failure before opening circuit
75
+ <br>*default value* : 5
76
+ <br>
78
77
 
78
+ * _:timeout_value_
79
+ <br>*description* : timeout value, if :timeout => true
80
+ <br>*default value* : 20
81
+ <br>
79
82
 
83
+ * _:timeout_
84
+ <br>*description* : (de)activate internal timeout
85
+ <br>*default value* : false
86
+ <br>
80
87
 
81
- puts "Circuits list"
82
- pp Sc4ry::Circuits::list
88
+ * _:max_timeout_count_
89
+ <br>*description* : maximum timeout try before opening circuit
90
+ <br>*default value** : 5
91
+ <br>
83
92
 
84
- # Config an alternate logger
85
- Sc4ry::Logger.register name: :perso, instance: ::Logger.new('/tmp/logfile.log')
86
- Sc4ry::Logger::current = :stdout
93
+ * _:max_time_
94
+ <br>*description* : maximum time for a circuit run
95
+ <br>*default value* : 10
96
+ <br>
87
97
 
98
+ * _:max_overtime_count_
99
+ <br>*description* : maximum count of overtime before opening circuit
100
+ <br>*default value* : 3
101
+ <br>
88
102
 
89
- # default values, circuit is half open before one of the max count is reached
103
+ * _:check_delay_
104
+ <br>*description* : delay after opening, before trying again to closed circuit or after an other check
105
+ <br>*default value* : 30
106
+ <br>
90
107
 
91
- # DEFAULT_CONFIG = {
92
- # :max_failure_count => 5,
93
- # :timeout_value => 20,
94
- # :timeout => false,
95
- # :max_timeout_count => 5,
96
- # :max_time => 10,
97
- # :max_overtime_count => 3,
98
- # :check_delay => 30,
99
- # :notifiers => [],
100
- # :forward_unknown_exceptions => true,
101
- # :raise_on_opening => false,
102
- # :exceptions => [StandardError, RuntimeError]
103
- # }
108
+ * _:notifiers_
109
+ <br>*description* : active notifier, must be :symbol in [:prometheus, :mattermost]
110
+ <br>*default value* : []
111
+ <br>
104
112
 
105
- # display configuration for a specific circuit
106
- Sc4ry::Circuits::list.each do |circuit|
107
- puts "Config #{circuit} :"
108
- pp Sc4ry::Circuits.get circuit: circuit
109
- end
113
+ * _:forward_unknown_exceptions_
114
+ <br>*description* : (de)activate forwarding of unknown exceptions, just log in DEBUG if false
115
+ <br>*default value* : true
116
+ <br>
110
117
 
111
- # sample Mattermost notification
112
- #Sc4ry::Notifiers::config({:name => :mattermost, :config => {:url => 'https://mattermost.mycorp.com', :token => "<TOKEN>"}})
118
+ * _:raise_on_opening_
119
+ <br>*description* : (de)activate raise specific Sc4ry exceptions ( CircuitBreaked ) if circuit opening
120
+ <br>*default value* : false
121
+ <br>
113
122
 
114
- # sample loop
115
- 100.times do
116
- sleep 1
117
- Sc4ry::Circuits.run circuit: :test do
118
- # for the test choose or build an endpoint you must shutdown
119
- puts RestClient.get('http://<URL_OF_AN_ENDPOINT>')
120
- end
121
- end
123
+ * _:exceptions_
124
+ <br>*description* : [StandardError, RuntimeError]
125
+ <br>*default value* : list of selected Exceptions considered for failure, others are SKIPPED.
126
+
127
+ ### Global overview for all features
128
+
129
+ This script could be usefull to test all feature on your installation
130
+
131
+ You need:
132
+ * Redis
133
+ * Prometheus pushgateway
134
+ * an endpoint if you want to check real endpoint
135
+ * a Mattermost/Slack incoming webhook (optional)
136
+
137
+ You could gate all this pre-requistes simply if you jave docker up on your machine
138
+
139
+ ```
140
+ $ docker pull redis:latest
141
+ $ docker run --rm -d -p 6379:6379/tcp redis:latest
142
+ $ docker pull prom/pushgateway:latest
143
+ $ docker run --rm -d -p 9091:9091 prom/pushgateway:latest
144
+ $ git clone https://github.com/Ultragreen/MockWS.git
145
+ $ cd MockWS
146
+ $ rackup
147
+ ```
148
+
149
+ create the test script like :
150
+
151
+ ```ruby
152
+
153
+ require 'rubygems'
154
+ require 'sc4ry'
155
+
156
+
157
+ include Sc4ry
158
+
159
+ # display of default Sc4ry config
160
+ puts '1.1/ CONFIG : Initial default config'
161
+ Circuits.default_config.each do |item,value|
162
+ puts " * #{item} : #{value}"
163
+ end
164
+ puts ''
165
+
166
+ # testing the two ways to configure Sc4ry default config
167
+ puts "1.2/ CONFIG : modifying default config activate timout and set max_time to 12"
168
+ Circuits.merge_default_config diff: {timeout: true }
169
+ Circuits.configure do |spec|
170
+ spec.max_time = 12
171
+ end
172
+ puts ''
173
+
174
+ # display default config, must be override with a nested hash by calling default_config= method
175
+ puts '1.3/ CONFIG : Default config updated:'
176
+ Circuits.default_config.each do |item,value|
177
+ puts " * #{item} : #{value}"
178
+ end
179
+ puts ''
180
+
181
+ # display current data Store
182
+ print "2.1/ STORE : Current datastore backend : "
183
+ puts Circuits.store.current
184
+ puts ''
185
+
186
+
187
+ # display available backend
188
+ puts "2.2/ STORE : List of existing backends : "
189
+ Circuits.store.list_backend.each do |backend|
190
+ puts " - #{backend}"
191
+ end
192
+ puts ''
193
+
194
+
195
+ # display Redis backend config in store before change
196
+ puts '2.3/ STORE : display default config of redis backend'
197
+ Circuits.store.get_config(backend: :redis).each do |item,value|
198
+ puts " * #{item} : #{value}"
199
+ end
200
+ puts ''
201
+
202
+ # reconfigure a backend
203
+ puts "2.4/ STORE : reconfigure Redis backend"
204
+ Circuits.store.config_backend name: :redis, config: {:host => 'localhost', :port => 6379, :db => 10 }
205
+ puts
122
206
 
207
+ # display after
208
+ puts '2.5/ STORE : display altered config of redis backend'
209
+ Circuits.store.get_config(backend: :redis).each do |item,value|
210
+ puts " * #{item} : #{value}"
211
+ end
212
+ puts ''
213
+
214
+
215
+ # change backend
216
+
217
+ puts '2.6/ STORE : change to redis backend (NEED a Redis installed) '
218
+ puts " $ docker pull redis:latest"
219
+ puts " $ docker run --rm -d -p 6379:6379/tcp redis:latest"
220
+ Circuits.store.change_backend name: :redis
221
+ puts ''
222
+
223
+ puts '2.7/ STORE : flush redis backend, just for test, and for idempotency (NEED a Redis installed) '
224
+ Circuits.store.flush
225
+ puts ''
226
+
227
+ # defining a circuit, config must be empty or override from default
228
+ puts "3.1/ CIRCUIT : registering a circuit by merge :"
229
+ Circuits.register circuit: :test, config: {:notifiers => [:prometheus,:mattermost], :exceptions => [Errno::ECONNREFUSED, URI::InvalidURIError] }
230
+ puts ""
231
+
232
+ puts "3.2/ CIRCUIT : registering a circuit by block :"
233
+ Circuits.register circuit: :test2 do |spec|
234
+ spec.exceptions = [Errno::ECONNREFUSED]
235
+ end
236
+ puts ''
237
+
238
+ puts "3.3/ CIRCUIT : registering a circuit by default :"
239
+ Circuits.register circuit: :test3
240
+ puts ''
241
+
242
+ puts "3.4/ CIRCUITS : Circuits list"
243
+ Circuits::list.each do |circuit|
244
+ puts " * #{circuit}"
245
+ end
246
+ puts ""
247
+
248
+ puts "3.5/ CIRCUIT : display a circuit config :test3 :"
249
+ Circuits.get(circuit: :test3).each do |item,value|
250
+ puts " * #{item} : #{value}"
251
+ end
252
+ puts ""
253
+
254
+ puts "3.6/ CIRCUIT : update config of :test3 => :raise_on_opening == true :"
255
+ Circuits.update_config circuit: :test3, config: {raise_on_opening: true}
256
+ puts ''
257
+
258
+ puts "3.7/ CIRCUIT : display a circuit config :test3 after change :"
259
+ Circuits.get(circuit: :test3).each do |item,value|
260
+ puts " * #{item} : #{value}"
261
+ end
262
+ puts ""
263
+
264
+
265
+ puts "3.8/ unregister a circuit : :test2 :"
266
+ Circuits.unregister circuit: :test2
267
+ puts ''
268
+
269
+ puts "3.9/ CIRCUITS : Circuits list after unregister"
270
+ Circuits::list.each do |circuit|
271
+ puts " * #{circuit}"
272
+ end
273
+ puts ""
274
+
275
+ # Config an alternate logger
276
+
277
+ puts "4.1/ LOGGER : register a logger on file "
278
+ Circuits.loggers.register name: :perso, instance: ::Logger.new('/tmp/logfile.log')
279
+ puts ''
280
+
281
+ puts "4.2/ LOGGER : get the list of available loggers"
282
+ Circuits.loggers.list_available.each do |logger|
283
+ puts " * #{logger}"
284
+ end
285
+ puts ''
286
+
287
+ puts "4.3/ LOGGER : change logger to :perso"
288
+ Circuits.loggers.current = :perso
289
+ puts ""
290
+
291
+
292
+
293
+
294
+
295
+ # sample Mattermost notification
296
+ puts "5/ set notifier mattermost on dummy url, change with your slack or mattermost server"
297
+ Sc4ry::Notifiers::config name: :mattermost, config: {:url => 'https://mattermost.mycorp.com', :token => "<TOKEN>"}
298
+ puts ""
299
+
300
+
301
+ # sample loop
302
+ puts "6/ running circuits test, please wait ... (see /tmp/logfile.log for result)"
303
+ puts " check endoint status for different result, you cloud use http://github.com/Ultragreen/MockWS for testing endpoint, on an other tty"
304
+ puts " $ git clone https://github.com/Ultragreen/MockWS.git"
305
+ puts " $ cd MockWS"
306
+ puts " $ rackup"
307
+ begin
308
+ 10.times do
309
+ sleep 1
310
+ Circuits.run circuit: :test do
311
+ # for the test choose or build an endpoint you must shutdown
312
+ puts RestClient.get('http://localhost:9292/test2/data')
313
+ end
314
+ end
315
+ rescue Interrupt
316
+ puts 'Interrputed'
317
+ ensure
318
+ Circuits.store.flush
319
+ end
320
+
321
+ puts "end"
123
322
  ```
124
323
 
125
324
  ## Development
data/Rakefile CHANGED
@@ -1,37 +1,44 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
  require 'yard'
4
- require 'yard/rake/yardoc_task.rb'
6
+ require 'yard/rake/yardoc_task'
5
7
  require 'code_statistics'
6
- require "roodi"
7
- require "roodi_task"
8
- require "version"
8
+ require 'version'
9
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
-
10
+ require 'rubocop//rake_task'
18
11
 
12
+ Rake::VersionTask.new
13
+ RuboCop::RakeTask.new
19
14
  RSpec::Core::RakeTask.new(:spec)
20
15
 
21
- task :default => :spec
22
-
16
+ task default: :spec
23
17
 
24
18
  YARD::Rake::YardocTask.new do |t|
25
- t.files = [ 'lib/**/*.rb', '-', 'doc/**/*','spec/**/*_spec.rb']
26
- t.options += ['-o', "yardoc"]
19
+ t.files = ['lib/**/*.rb', '-', 'doc/**/*', 'spec/**/*_spec.rb']
20
+ t.options += ['-o', 'yardoc']
27
21
  end
28
22
  YARD::Config.load_plugin('yard-rspec')
29
-
23
+
30
24
  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
25
+ task :clobber do
26
+ begin
27
+ rm_r 'yardoc'
28
+ rescue StandardError
29
+ nil
30
+ end
31
+ begin
32
+ rm_r '.yardoc'
33
+ rescue StandardError
34
+ nil
35
+ end
36
+ begin
37
+ rm_r 'pkg'
38
+ rescue StandardError
39
+ nil
40
+ end
41
+ end
35
42
  end
36
- end
37
- task :clobber => "yardoc:clobber"
43
+
44
+ task clobber: 'yardoc:clobber'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.1
Binary file
data/bin/console CHANGED
@@ -1,7 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require "bundler/setup"
4
- require "sc4ry"
4
+ require 'bundler/setup'
5
+ require 'sc4ry'
5
6
 
6
7
  # You can add fixtures and/or initialization code here to make experimenting
7
8
  # with your gem easier. You can also use a different console, if you like.
@@ -10,5 +11,5 @@ require "sc4ry"
10
11
  # require "pry"
11
12
  # Pry.start
12
13
 
13
- require "irb"
14
+ require 'irb'
14
15
  IRB.start(__FILE__)
@@ -1 +1,3 @@
1
- Dir[File.dirname(__FILE__) + '/*.rb'].sort.each { |file| require file unless File.basename(file) == 'init.rb' }
1
+ # frozen_string_literal: true
2
+
3
+ Dir["#{File.dirname(__FILE__)}/*.rb"].sort.each { |file| require file unless File.basename(file) == 'init.rb' }
@@ -1,48 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Sc4ry module
2
4
  # @note namespace
3
5
  module Sc4ry
4
-
5
6
  # Sc4ry:Backends module
6
7
  # @note namespace
7
8
  module Backends
8
-
9
9
  # class of the memory backend
10
10
  class Memory
11
-
12
11
  # Constructor
13
- # @param [Hash] config Config map
12
+ # @param [Hash] config Config map
14
13
  # @return [Sc4ry::Backends::Memory] a in Memory backend
15
- def initialize(config=nil?)
16
- @data = Hash::new
14
+ def initialize(_config = nil?)
15
+ @data = {}
17
16
  end
18
17
 
19
18
  # return the list of find records in backend for a specific pattern
20
19
  # @return [Array] list of record (for all hostname if hostname is specified)
21
20
  def list
22
- return @data.keys
21
+ @data.keys
23
22
  end
24
23
 
25
24
  # return value of queried record
26
25
  # @param key [Symbol] the name of the record
27
26
  # @return [String] content value of record
28
- def get(key: )
29
- return @data[key]
27
+ def get(key:)
28
+ @data[key]
30
29
  end
31
30
 
32
31
  # defined and store value for specified key
33
32
  # @param key [Symbol] :key the name of the record
34
33
  # @param value [Symbol] :value the content value of the record
35
34
  # @return [String] content value of record
36
- def put(key:, value: )
35
+ def put(key:, value:)
37
36
  @data[key] = value
38
- end
37
+ end
39
38
 
40
39
  # delete a specific record
41
40
  # @param params [Symbol] the name of the record
42
- # @return [Boolean] status of the operation
43
- def del(key: )
41
+ # @return [Boolean] status of the operation
42
+ def del(key:)
44
43
  @data.delete key
45
- end
44
+ end
46
45
 
47
46
  # flush all records in backend
48
47
  # @return [Boolean] status of the operation
@@ -53,10 +52,9 @@ module Sc4ry
53
52
  # verifiy a specific record existence
54
53
  # @param key [Symbol] the name of the record
55
54
  # @return [Boolean] presence of the record
56
- def exist?(key: )
57
- return @data.include? key
55
+ def exist?(key:)
56
+ @data.include? key
58
57
  end
59
-
60
58
  end
61
59
  end
62
- end
60
+ end