sc4ry 0.1.6 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,29 +1,56 @@
1
1
  Dir[File.dirname(__FILE__) + '/*.rb'].sort.each { |file| require file unless File.basename(file) == 'init.rb' }
2
2
 
3
+ # Sc4ry module
4
+ # @note namespace
3
5
  module Sc4ry
4
- module Notifiers
5
- @@notifiers_list = {:prometheus => {:class => Sc4ry::Notifiers::Prometheus, :config => {:url => 'http://localhost:9091'}},
6
- :mattermost => {:class => Sc4ry::Notifiers::Mattermost, :config => {:url => 'http://localhost:9999', :token => "<CHANGE_ME>"}}
7
- }
8
6
 
7
+ # Sc4ry::Notifiers module
8
+ # @note namespace
9
+ module Notifiers
10
+
11
+ # default notifiers specifications
12
+ DEFAULT_NOTIFIERS = {:prometheus => {:class => Sc4ry::Notifiers::Prometheus, :config => {:url => 'http://localhost:9091'}},
13
+ :mattermost => {:class => Sc4ry::Notifiers::Mattermost, :config => {:url => 'http://localhost:9999', :token => "<CHANGE_ME>"}}
14
+ }
15
+ @@notifiers_list = DEFAULT_NOTIFIERS.dup
16
+
17
+ # class method how display a specific notifier config
18
+ # @param notifier [Symbol] a notifier name
19
+ # @return [Hash] the config
20
+ def Notifiers.display_config(notifier: )
21
+ raise Sc4ry::Exceptions::Sc4ryNotifierError, "Notifier #{notifier} not found" unless @@notifiers_list.include? notifier
22
+ return @@notifiers_list[notifier][:config]
23
+ end
24
+
25
+ # class method how return the list of known notifiers
26
+ # @return [Array] a list of [Symbol] notifiers name
9
27
  def Notifiers.list
10
28
  return @@notifiers_list.keys
11
29
  end
12
30
 
13
- def Notifiers.get(options ={})
14
- return @@notifiers_list[options[:name]]
31
+ # class method how return a specific notifier by name
32
+ # @param name [Symbol] a notifier name
33
+ # @return [Hash] the notifier structure
34
+ def Notifiers.get(name: )
35
+ return @@notifiers_list[name]
15
36
  end
16
37
 
17
- def Notifiers.register(options)
18
- raise ":name is mandatory" unless options[:name]
19
- raise ":definition is mandatory" unless options[:definition]
20
- @@notifiers_list[options[:name]] = options[:definition]
38
+ # class method how register a specific notifier
39
+ # @param name [Symbol] a notifier name
40
+ # @param definition [Hash] a notifier definition
41
+ # @return [Hash] the notifier structure
42
+ def Notifiers.register(name: , definition: )
43
+ @@notifiers_list[name] = definition
21
44
  end
22
45
 
23
- def Notifiers.config(options)
24
- raise ":name is mandatory" unless options[:name]
25
- raise ":config is mandatory" unless options[:config]
26
- @@notifiers_list[options[:name]][:config] = options[:config]
46
+
47
+ # class method how configure a specific notifier
48
+ # @param name [Symbol] a notifier name
49
+ # @param config [Hash] a notifier config
50
+ # @return [Hash] the notifier structure
51
+ def Notifiers.config(name:, config: )
52
+ @@notifiers_list[name][:config] = config
53
+ return config
27
54
  end
28
55
  end
29
56
  end
@@ -1,33 +1,41 @@
1
+ # Sc4ry module
2
+ # @note namespace
1
3
  module Sc4ry
4
+ # Sc4ry::Notifiers module
5
+ # @note namespace
2
6
  module Notifiers
3
7
 
8
+ # Mattermost Notifier class
4
9
  class Mattermost
5
10
 
6
11
  # send metrics to Prometheus PushGateway
7
12
  # @return [Bool]
8
13
  def Mattermost.notify(options = {})
9
- config = Sc4ry::Notifiers.get({name: :mattermost})[:config]
14
+ config = Sc4ry::Notifiers.get(name: :mattermost)[:config]
10
15
  status = options[:config][:status][:general]
11
16
  circuit = options[:circuit]
12
17
  status_map = {:open => 0, :half_open => 1, :closed => 2}
13
-
14
- uri = URI.parse("#{config[:url]}/hooks/#{config[:token]}")
15
- message = "notifying for circuit #{circuit.to_s}, state : #{status.to_s}."
16
- if Sc4ry::Helpers::verify_service url: config[:url] then
17
- request = ::Net::HTTP::Post.new(uri)
18
- request.content_type = "application/json"
19
- req_options = {
20
- use_ssl: uri.scheme == "https",
21
- }
22
- payload = { "text" => "message : #{message } from #{Socket.gethostname}", "username" => "Sc4ry" }
23
- Sc4ry::Helpers.log level: :debug, message: "Mattermost Notifier : #{message}"
24
- request.body = ::JSON.dump(payload)
25
- response = ::Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
26
- http.request(request)
18
+ begin
19
+ uri = URI.parse("#{config[:url]}/hooks/#{config[:token]}")
20
+ message = "notifying for circuit #{circuit.to_s}, state : #{status.to_s}."
21
+ if Sc4ry::Helpers::verify_service url: config[:url] then
22
+ request = ::Net::HTTP::Post.new(uri)
23
+ request.content_type = "application/json"
24
+ req_options = {
25
+ use_ssl: uri.scheme == "https",
26
+ }
27
+ payload = { "text" => "message : #{message } from #{Socket.gethostname}", "username" => "Sc4ry" }
28
+ Sc4ry::Helpers.log level: :debug, message: "Mattermost Notifying : #{message}"
29
+ request.body = ::JSON.dump(payload)
30
+ response = ::Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
31
+ http.request(request)
32
+ end
33
+
34
+ else
35
+ Sc4ry::Helpers.log level: :warn, message: "Mattermost Notifier : can't notify Mattermost not reachable."
27
36
  end
28
-
29
- else
30
- Sc4ry::Helpers.log level: :warn, message: "Mattermost Notifier : can't notify Mattermost not reachable."
37
+ rescue URI::InvalidURIError
38
+ Sc4ry::Helpers.log level: :warn, message: "Mattermost Notifier : URL malformed"
31
39
  end
32
40
  end
33
41
  end
@@ -1,7 +1,13 @@
1
+ # Sc4ry module
2
+ # @note namespace
1
3
  module Sc4ry
4
+
5
+ # Sc4ry::Notifiers module
6
+ # @note namespace
2
7
  module Notifiers
3
8
 
4
9
 
10
+ # Prometheus notifier class
5
11
  class Prometheus
6
12
 
7
13
  @@registry = ::Prometheus::Client::Registry::new
@@ -14,7 +20,7 @@ module Sc4ry
14
20
  # send metrics to Prometheus PushGateway
15
21
  # @return [Bool]
16
22
  def Prometheus.notify(options = {})
17
- @config = Sc4ry::Notifiers.get({name: :prometheus})[:config]
23
+ @config = Sc4ry::Notifiers.get(name: :prometheus)[:config]
18
24
  status = options[:config][:status][:general]
19
25
  circuit = options[:circuit]
20
26
  status_map = {:open => 0, :half_open => 1, :closed => 2}
@@ -1,9 +1,16 @@
1
1
 
2
+ # Sc4ry module
3
+ # @note namespace
2
4
  module Sc4ry
5
+
6
+ # class Facility to run and update values/status for a circuit Proc
3
7
  class RunController
4
8
 
9
+ # return the execution time of the proc
5
10
  attr_reader :execution_time
6
11
 
12
+ # constructor
13
+ # @param [Hash] circuit the data of the circuit
7
14
  def initialize(circuit = {})
8
15
  @circuit = circuit
9
16
  @execution_time = 0
@@ -12,38 +19,47 @@ module Sc4ry
12
19
  @overtime = false
13
20
  end
14
21
 
22
+ # return if the Proc failed on a covered exception by this circuit
23
+ # @return [Boolean]
15
24
  def failed?
16
25
  return @failure
17
26
  end
18
-
27
+
28
+ # return if the Proc overtime the specified time of the circuit
29
+ # @return [Boolean]
19
30
  def overtimed?
20
31
  return @overtime
21
32
  end
22
33
 
34
+ # return if the Proc timeout the timeout defined value of the circuit, if timeout is active
35
+ # @return [Boolean]
23
36
  def timeout?
24
37
  return @timeout
25
38
  end
26
39
 
27
-
28
- def run(options = {})
40
+ # run and update values for the bloc given by keyword
41
+ # @param [Proc] block a block to run and calculate
42
+ # @return [Hash] a result Hash
43
+ def run(block: )
29
44
  start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC)
30
45
  begin
31
46
  if @circuit[:timeout] == true
32
47
  Timeout::timeout(@circuit[:timeout_value]) do
33
- options[:block].call
48
+ block.call
34
49
  end
35
50
  @timeout = false
36
51
  else
37
- options[:block].call
52
+ block.call
38
53
  end
39
54
  rescue Exception => e
40
- @last_exception = e.class
55
+ @last_exception = e.class.to_s
41
56
  if e.class == Timeout::Error then
42
57
  @timeout = true
43
58
  elsif @circuit[:exceptions].include? e.class
44
59
  @failure = true
45
60
  else
46
61
  if @circuit[:forward_unknown_exceptions] then
62
+
47
63
  raise e.class, "Sc4ry forward: #{e.message}"
48
64
  else
49
65
  Sc4ry::Helpers.log level: :debug, message: "skipped : #{@last_exception}"
data/lib/sc4ry/store.rb CHANGED
@@ -1,4 +1,10 @@
1
+ # Sc4ry module
2
+ # @note namespace
1
3
  module Sc4ry
4
+
5
+ # Sc4ry::Store class
6
+ # Store Class Provider/manager singleton Forwarder on {Sc4ry::Backends::Memory} or {Sc4ry::Backends::Redis}
7
+ # @note must be accessed by {Sc4ry::Circuits.store}
2
8
  class Store
3
9
 
4
10
  @@current = :memory
@@ -6,35 +12,92 @@ module Sc4ry
6
12
  extend Forwardable
7
13
  include Singleton
8
14
 
9
- @@backends = {:memory => {:class => Sc4ry::Backends::Memory},
15
+ @@backends = {:memory => {:class => Sc4ry::Backends::Memory, config: {}},
10
16
  :redis => {:class => Sc4ry::Backends::Redis, :config => {:host => 'localhost', :port => 6379, :db => 10 }}}
11
17
 
18
+ # accessor on current backend (default :memory)
12
19
  attr_reader :be
20
+
13
21
  def_delegators :@be, :put, :get, :flush, :exist?, :del, :list
14
22
 
23
+ # constructor pointing on :memory backend
15
24
  def initialize
16
25
  change_backend name: @@current
17
26
  end
18
27
 
28
+ # return the current backend
29
+ # @return [Object] in {Sc4ry::Backends::Memory} or {Sc4ry::Backends::Redis}
30
+ # @example usage
31
+ # include Sc4ry
32
+ # puts Circuits.store.current
19
33
  def current
20
34
  return @@current
21
35
  end
22
36
 
23
- def change_backend(options)
24
- @@current = options[:name]
37
+ # return the config of a specific backend
38
+ # @param [Symbol] backend the name the backend
39
+ # @return [Hash] the config of the backend
40
+ # @raise Sc4ry::Exceptions::Sc4ryBackendError if backend is not found
41
+ # @example usage
42
+ # include Sc4ry
43
+ # puts Circuits.store.get_config backend: :redis
44
+ def get_config(backend: )
45
+ raise Sc4ry::Exceptions::Sc4ryBackendError, "backend #{backend} not found" unless @@backends.include? backend
46
+ return @@backends[backend][:config]
47
+ end
48
+
49
+ # list backend available
50
+ # @return [Array] of Symbol the list of backend name
51
+ # @example usage
52
+ # include Sc4ry
53
+ # puts Circuits.store.list_backend
54
+ def list_backend
55
+ return @@backends.keys
56
+ end
57
+
58
+ # change the current backend
59
+ # @note if changing form :memory to :redis => all values and result are lost and circuits will be lost
60
+ # @note if changing to :redis, get all the define circuits with values and status (ideal) for distributed worker/instance/runner/services
61
+ # @param [Symbol] name the name of the target backend
62
+ # @return [Symbol] the name of the new current backend
63
+ # @raise Sc4ry::Exceptions::Sc4ryBackendError if backend is not found
64
+ def change_backend(name: )
65
+ raise Sc4ry::Exceptions::Sc4ryBackendError, "backend #{name} not found" unless @@backends.include? name
66
+ @@current = name
25
67
  @be = @@backends[@@current][:class]::new(@@backends[@@current][:config])
68
+ return name
26
69
  end
27
70
 
28
- def register_backend(options)
29
- raise ":name is mandatory" unless options[:name]
30
- raise ":definition is mandatory" unless options[:definition]
31
- @@backends[options[:name]] = options[:definition]
71
+ # register a new backend
72
+ # @param [Symbol] name the name of the backend
73
+ # @param [Hash] config the config for this backend
74
+ # @param [Class] backend_class the class name of the new backend
75
+ # @raise Sc4ry::Exceptions::Sc4ryBackendError if backend already exist
76
+ # @return [Symbol] the name of the backend
77
+ def register_backend(name:, config: {}, backend_class:)
78
+ raise Sc4ry::Exceptions::Sc4ryBackendError , "backend #{name} already exist" if @@backends.include? name
79
+ @@backends[name] = {config: config, class: backend_class}
80
+ return name
32
81
  end
33
82
 
34
- def config_backend(options)
35
- raise ":name is mandatory" unless options[:name]
36
- raise ":config is mandatory" unless options[:config]
37
- @@backends[options[:name]][:config] = options[:config]
83
+ # delete the specified backend reference
84
+ # @param [Symbol] name the name of the target backend
85
+ # @raise Sc4ry::Exceptions::Sc4ryBackendError if backend is not found, or name == :memory or :redis
86
+ # @return [Boolean]
87
+ def delete_backend(name: )
88
+ raise Sc4ry::Exceptions::Sc4ryBackendError , "Delete forbidden for backend in [:redis,:memory]" if [:memory,:redis].include? name
89
+ raise Sc4ry::Exceptions::Sc4ryBackendError , "backend #{name} not found" unless @@backends.include? name
90
+ return @@backends.delete(name)
91
+ end
92
+
93
+ # change the specified backend config
94
+ # @param [Symbol] name the name of the target backend
95
+ # @param [Hash] config the config of the specified backend
96
+ # @raise Sc4ry::Exceptions::Sc4ryBackendError if backend is not found, or name == :memory
97
+ def config_backend(name:, config:)
98
+ raise Sc4ry::Exceptions::Sc4ryBackendError, "backend #{name} not found" unless @@backends.include? name
99
+ raise Sc4ry::Exceptions::Sc4ryBackendError, "backend :memory not need config" if name == :memory
100
+ @@backends[name][:config] = config
38
101
  end
39
102
 
40
103
 
data/lib/sc4ry/version.rb CHANGED
@@ -1,3 +1,9 @@
1
+ require 'version'
2
+
3
+ # Sc4ry module
4
+ # @note namespace
1
5
  module Sc4ry
2
- VERSION = "0.1.6"
6
+
7
+ # the version of Sc4ry
8
+ VERSION = Version.current
3
9
  end
data/sc4ry.gemspec CHANGED
@@ -1,8 +1,7 @@
1
- require_relative 'lib/sc4ry/version'
2
1
 
3
2
  Gem::Specification.new do |spec|
4
3
  spec.name = "sc4ry"
5
- spec.version = Sc4ry::VERSION
4
+ spec.version = `cat VERSION`.chomp
6
5
  spec.authors = ["Romain GEORGES"]
7
6
  spec.email = ["romain.georges@orange.com"]
8
7
 
@@ -22,6 +21,11 @@ Gem::Specification.new do |spec|
22
21
 
23
22
  spec.add_dependency "prometheus-client", "~> 3.0"
24
23
  spec.add_dependency "rest-client", "~> 2.1"
24
+ spec.add_dependency "redis", "~> 4.6"
25
25
 
26
-
26
+ spec.add_development_dependency "yard", "~> 0.9.27"
27
+ spec.add_development_dependency 'code_statistics', '~> 0.2.13'
28
+ spec.add_development_dependency "yard-rspec", "~> 0.1"
29
+ spec.add_development_dependency "roodi", "~> 5.0"
30
+ spec.add_dependency "version", "~> 1.1"
27
31
  end
@@ -0,0 +1,25 @@
1
+ AssignmentInConditionalCheck:
2
+ CaseMissingElseCheck:
3
+ ClassLineCountCheck:
4
+ line_count: 300 # including comments yard
5
+ ClassNameCheck:
6
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
7
+ #ClassVariableCheck:
8
+ CyclomaticComplexityBlockCheck:
9
+ complexity: 5
10
+ CyclomaticComplexityMethodCheck:
11
+ complexity: 10
12
+ EmptyRescueBodyCheck:
13
+ ForLoopCheck:
14
+ MethodLineCountCheck:
15
+ line_count: 30
16
+ MethodNameCheck:
17
+ pattern: !ruby/regexp /^[_a-z<>=\[|+-\/\*`]+[_a-z0-9_<>=~@\[\]]*[=!\?]?$/
18
+ # MissingForeignKeyIndexCheck:
19
+ ModuleLineCountCheck:
20
+ line_count: 300
21
+ ModuleNameCheck:
22
+ pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
23
+ ParameterNumberCheck:
24
+ parameter_count: 5
25
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sc4ry
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Romain GEORGES
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-08 00:00:00.000000000 Z
11
+ date: 2022-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prometheus-client
@@ -38,6 +38,90 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: redis
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.6'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.6'
55
+ - !ruby/object:Gem::Dependency
56
+ name: yard
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.9.27
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.9.27
69
+ - !ruby/object:Gem::Dependency
70
+ name: code_statistics
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.13
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.13
83
+ - !ruby/object:Gem::Dependency
84
+ name: yard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '0.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '0.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: roodi
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '5.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '5.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: version
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '1.1'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '1.1'
41
125
  description: Sc4ry provide the design pattern Circuit breaker for your application.
42
126
  email:
43
127
  - romain.georges@orange.com
@@ -45,14 +129,16 @@ executables: []
45
129
  extensions: []
46
130
  extra_rdoc_files: []
47
131
  files:
132
+ - ".github/workflows/main.yml"
48
133
  - ".gitignore"
49
134
  - ".rspec"
50
135
  - Gemfile
51
136
  - LICENSE.txt
52
137
  - README.md
53
138
  - Rakefile
139
+ - VERSION
54
140
  - assets/images/logo_sc4ry.png
55
- - assets/logo_sc4ry.png
141
+ - assets/images/sc4ry_workflow.png
56
142
  - bin/console
57
143
  - bin/setup
58
144
  - lib/sc4ry.rb
@@ -60,6 +146,8 @@ files:
60
146
  - lib/sc4ry/backends/memory.rb
61
147
  - lib/sc4ry/backends/redis.rb
62
148
  - lib/sc4ry/circuits.rb
149
+ - lib/sc4ry/config.rb
150
+ - lib/sc4ry/constants.rb
63
151
  - lib/sc4ry/dependencies.rb
64
152
  - lib/sc4ry/exceptions.rb
65
153
  - lib/sc4ry/exporters/init.rb
@@ -72,11 +160,12 @@ files:
72
160
  - lib/sc4ry/store.rb
73
161
  - lib/sc4ry/version.rb
74
162
  - sc4ry.gemspec
163
+ - ultragreen_roodi_coding_convention.yml
75
164
  homepage: https://github.com/Ultragreen/sc4ry
76
165
  licenses:
77
166
  - MIT
78
167
  metadata: {}
79
- post_install_message:
168
+ post_install_message:
80
169
  rdoc_options: []
81
170
  require_paths:
82
171
  - lib
@@ -91,8 +180,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
180
  - !ruby/object:Gem::Version
92
181
  version: '0'
93
182
  requirements: []
94
- rubygems_version: 3.1.2
95
- signing_key:
183
+ rubygems_version: 3.2.3
184
+ signing_key:
96
185
  specification_version: 4
97
186
  summary: Sc4Ry is Simple Circuitbreaker 4 RubY
98
187
  test_files: []
Binary file