sc4ry 0.1.7 → 0.1.8

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: 86e9653534207c3b9809325a04d64da78f0636a1e62d800729cc15737ae0acf3
4
- data.tar.gz: 1c00863a8330914acf9aea73f6022d7afaef73be50b3341f01e21a7490244125
3
+ metadata.gz: 19791a7d72756fe96464fd43fd648010a71ff7491b96702da23c8828c19f58a2
4
+ data.tar.gz: 6e1d7318a3de28fd0950213f9e8bf344819dc5858f66f5ce0ec5b1eb7275b99f
5
5
  SHA512:
6
- metadata.gz: 37c78c76dbb4a2a636ebbeb2bd70ac45949aa89983adda27810fb0bc3cf49078ef2e94e10b02f105ee6354109e2efeef9ed3f2b22678645fcf79e0cf097f0536
7
- data.tar.gz: e32fe7cb84723bfcbbb1a90634d55cd3922c42bedb3e98c93d80f817ee6236ca966196c067a410a44affd04ff00c21560cdf18c702ad8e3f727e37ff9b248fbe
6
+ metadata.gz: 7f2c87ddb8ee4dc04aa1d6b2b9b9f214309fc56749dbb85a648f541ca1c27621dffdfcb9619348c3cd57874f5f9e9f0c24371fc9926150d32bb62edf3d5e4798
7
+ data.tar.gz: b43b8a8a6611fa76c890b10ccc40ffbee0f035bf8af298891919dbb9b4bf3ece9fb1ef13fb5386f0807c11e188d6cf7e9448979a4dc3552a62d1043cf53c3a78
data/README.md CHANGED
@@ -30,39 +30,64 @@ require 'rubygems'
30
30
  require 'sc4ry'
31
31
 
32
32
 
33
+ puts 'Initial default config'
34
+ pp Sc4ry::Circuits.default_config
35
+
36
+
37
+ Sc4ry::Circuits.merge_default_config diff: {timeout: true }
38
+ # or with a block
39
+ Sc4ry::Circuits.configure do |spec|
40
+ spec.max_time = 12
41
+ end
42
+
43
+
44
+ # display default config
45
+ puts 'Default config'
46
+ pp Sc4ry::Circuits.default_config
47
+
48
+
33
49
  # defining a circuit, config must be empty or override from default
34
- Sc4ry::Circuits.register({:circuit =>:test, :config => {:notifiers => [:prometheus, :mattermost], :exceptions => [Errno::ECONNREFUSED], :timeout => true, :timeout_value => 3, :check_delay => 5 }})
50
+ Sc4ry::Circuits.register circuit: :test, config: {:notifiers => [:prometheus,:mattermost], :exceptions => [Errno::ECONNREFUSED, URI::InvalidURIError] }
51
+ # or with a block
52
+ Sc4ry::Circuits.register circuit: :test2 do |spec|
53
+ spec.exceptions = [Errno::ECONNREFUSED]
54
+ end
55
+ # or
56
+ Sc4ry::Circuits.register circuit: :test3
35
57
 
36
- # display the list of known circuit
37
- pp Sc4ry::Circuits.list
38
58
 
39
- # display default config, must be override with a nested hash by calling default_config= method
40
- pp Sc4ry::Circuits.default_config
41
59
 
60
+ puts "Circuits list"
61
+ pp Sc4ry::Circuits::list
42
62
 
43
- # Config an alternate logger
63
+ # Config an alternate logger
44
64
  Sc4ry::Logger.register name: :perso, instance: ::Logger.new('/tmp/logfile.log')
45
- Sc4ry::Logger::current = :perso
65
+ Sc4ry::Logger::current = :stdout
46
66
 
47
67
 
48
68
  # default values, circuit is half open before one of the max count is reached
49
69
 
50
- # {:max_failure_count=>5, => maximum failure before opening circuit
51
- # :timeout_value=>20, => timeout value, if :timeout => true
52
- # :timeout=>false, => (de)activate internal timeout
53
- # :max_timeout_count=>5, => maximum timeout try before opening circuit
54
- # :max_time=>10, => maximum time for a circuit run
55
- # :max_overtime_count=>3, => maximum count of overtime before opening circuit
56
- # :check_delay=>30, => delay after opening, before trying again to closed circuit or after an other check
57
- # :notifiers=>[], => active notifier, must be :symbol in [:prometheus, :mattermost]
58
- # :forward_unknown_exceptions => true, => (de)activate forwarding of unknown exceptions, just log in DEBUG if false
59
- # :raise_on_opening => false, => (de)activate raise specific Sc4ry exceptions ( CircuitBreaked ) if circuit opening
60
- # :exceptions=>[StandardError, RuntimeError]} => list of selected Exceptions considered for failure, others are SKIPPED.
70
+ # DEFAULT_CONFIG = {
71
+ # :max_failure_count => 5,
72
+ # :timeout_value => 20,
73
+ # :timeout => false,
74
+ # :max_timeout_count => 5,
75
+ # :max_time => 10,
76
+ # :max_overtime_count => 3,
77
+ # :check_delay => 30,
78
+ # :notifiers => [],
79
+ # :forward_unknown_exceptions => true,
80
+ # :raise_on_opening => false,
81
+ # :exceptions => [StandardError, RuntimeError]
82
+ # }
61
83
 
62
84
  # display configuration for a specific circuit
63
- pp Sc4ry::Circuits.get circuit: :test
85
+ Sc4ry::Circuits::list.each do |circuit|
86
+ puts "Config #{circuit} :"
87
+ pp Sc4ry::Circuits.get circuit: circuit
88
+ end
64
89
 
65
- # sample Mattermost notification
90
+ # sample Mattermost notification
66
91
  #Sc4ry::Notifiers::config({:name => :mattermost, :config => {:url => 'https://mattermost.mycorp.com', :token => "<TOKEN>"}})
67
92
 
68
93
  # sample loop
@@ -70,7 +95,7 @@ pp Sc4ry::Circuits.get circuit: :test
70
95
  sleep 1
71
96
  Sc4ry::Circuits.run circuit: :test do
72
97
  # for the test choose or build an endpoint you must shutdown
73
- puts RestClient.get('http://<URL_OF_A_ENDPOINT>')
98
+ puts RestClient.get('http://<URL_OF_AN_ENDPOINT>')
74
99
  end
75
100
  end
76
101
 
@@ -1,37 +1,52 @@
1
1
  module Sc4ry
2
2
  class Circuits
3
3
 
4
+ include Sc4ry::Constants
5
+ include Sc4ry::Exceptions
6
+
4
7
  @@circuits_store = Sc4ry::Store.instance
5
8
 
6
- @@config = { :max_failure_count => 5,
7
- :timeout_value => 20,
8
- :timeout => false,
9
- :max_timeout_count => 5,
10
- :max_time => 10,
11
- :max_overtime_count => 3,
12
- :check_delay => 30,
13
- :notifiers => [],
14
- :forward_unknown_exceptions => true,
15
- :raise_on_opening => false,
16
- :exceptions => [StandardError, RuntimeError]
17
- }
9
+ @@config = DEFAULT_CONFIG
18
10
 
19
11
  def Circuits.default_config
20
12
  return @@config
21
13
  end
22
14
 
23
- def Circuits.default_config=(config)
24
- @@config = config
25
- @@config[:exceptions].map! {|item| item = Object.const_get item if item.class == String}
15
+ def Circuits.merge_default_config(diff:)
16
+ validator = Sc4ry::Config::Validator::new(definition: diff, from: @@config)
17
+ validator.validate!
18
+ @@config = validator.result
19
+
20
+ end
21
+
22
+ def Circuits.configure(&bloc)
23
+ mapper = Sc4ry::Config::ConfigMapper::new(definition: @@config.dup)
24
+ yield(mapper)
25
+ validator = Sc4ry::Config::Validator::new(definition: mapper.config, from: @@config)
26
+ validator.validate!
27
+ @@config = validator.result
28
+ end
29
+
26
30
 
31
+ def Circuits.default_config=(config)
32
+ Sc4ry::Helpers.log level: :warn, message: "DEPRECATED: Circuits.default_config= is deprecated please use Circuits.merge_default_config add: {<config_hash>}"
33
+ Circuits.merge_default_config(diff: config)
27
34
  end
28
35
 
29
- def Circuits.register(options = {})
30
- raise ":circuit is mandatory" unless options[:circuit]
31
- name = options[:circuit]
32
- override = (options[:config].class == Hash)? options[:config] : {}
33
- config = @@config.merge override
34
- @@circuits_store.put key: name, value: config
36
+ def Circuits.register(circuit:, config: {})
37
+ if config.size > 0 and block_given? then
38
+ raise Sc4ryGenericError, "config: keyword must not be defined when block is given"
39
+ end
40
+ if block_given? then
41
+ mapper = Sc4ry::Config::ConfigMapper::new(definition: @@config.dup)
42
+ yield(mapper)
43
+ validator = Sc4ry::Config::Validator::new(definition: mapper.config, from: @@config.dup)
44
+ else
45
+ validator = Sc4ry::Config::Validator::new(definition: config, from: @@config.dup )
46
+ end
47
+ validator.validate!
48
+ Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit} : registered"
49
+ @@circuits_store.put key: circuit, value: validator.result
35
50
  end
36
51
 
37
52
  def Circuits.list
@@ -45,10 +60,10 @@ module Sc4ry
45
60
 
46
61
  def Circuits.run(options = {}, &block)
47
62
  circuits_list = Circuits.list
48
- raise "No circuit block given" unless block_given?
49
- raise "No circuits defined" if circuits_list.empty?
63
+ raise Sc4ryGenericError, "No circuit block given" unless block_given?
64
+ raise Sc4ryGenericError, "No circuits defined" if circuits_list.empty?
50
65
  circuit_name = (options[:circuit])? options[:circuit] : circuits_list.first
51
- raise "Circuit #{circuit_name} not found" unless circuits_list.include? circuit_name
66
+ raise Sc4ryGenericError, "Circuit #{circuit_name} not found" unless circuits_list.include? circuit_name
52
67
  circuit = Circuits.get circuit: circuit_name
53
68
  skip = false
54
69
  if circuit.include? :status then
@@ -61,7 +76,7 @@ module Sc4ry
61
76
  controller = Sc4ry::RunController.new(circuit)
62
77
  Circuits.control circuit: circuit_name, values: controller.run(block: block)
63
78
  end
64
- Sc4ry::Helpers.log level: :info, message: "Circuit #{circuit_name} : status #{circuit[:status]}"
79
+ Sc4ry::Helpers.log level: :debug, message: "Circuit #{circuit_name} : status #{circuit[:status]}"
65
80
 
66
81
  end
67
82
 
@@ -95,7 +110,9 @@ module Sc4ry
95
110
  data[:status][:general] = status if worst_status.include? status
96
111
  end
97
112
  if save != data[:status][:general] then
98
- raise Sc4ry::Exceptions::CircuitBreaked if data[:status][:general] == :open and data[:raise_on_opening]
113
+ raise CircuitBreaked if data[:status][:general] == :open and data[:raise_on_opening]
114
+ Sc4ry::Helpers.log level: :error, message: "Circuit #{options[:circuit]} : breacking ! " if data[:status][:general] == :open
115
+ Sc4ry::Helpers.log level: :info, message: "Circuit #{options[:circuit]} : is now closed" if data[:status][:general] == :closed
99
116
  Sc4ry::Helpers.notify circuit: options[:circuit], config: data
100
117
  end
101
118
  @@circuits_store.put key: options[:circuit], value: data
@@ -0,0 +1,65 @@
1
+ module Sc4ry
2
+ module Config
3
+ class Validator
4
+ attr_reader :definition
5
+ attr_reader :default
6
+
7
+ def result
8
+ @default
9
+ end
10
+
11
+ include Sc4ry::Constants
12
+ include Sc4ry::Exceptions
13
+
14
+ def initialize(definition: , from: DEFAULT_CONFIG)
15
+ @default = from
16
+ @definition = definition
17
+ end
18
+
19
+ def validate!
20
+ unknown_keys = @definition.keys.difference @default.keys
21
+ raise ConfigError::new("Unknown keys in config set : #{unknown_keys.to_s}") unless unknown_keys.empty?
22
+ validate_formats
23
+ @default.merge! @definition
24
+ format_exceptions
25
+ end
26
+
27
+ private
28
+ def validate_formats
29
+ definition.each do |spec,value|
30
+ raise ConfigError::new("#{spec} value #{DEFAULT_CONFIG_FORMATS[spec][:desc]}") unless DEFAULT_CONFIG_FORMATS[spec][:proc].call(value)
31
+ if DEFAULT_CONFIG_FORMATS[spec].include? :list then
32
+ value.each do |item|
33
+ raise ConfigError::new("#{spec} value must be in #{DEFAULT_CONFIG_FORMATS[spec][:list]}") unless DEFAULT_CONFIG_FORMATS[spec][:list].include? item
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ def format_exceptions
40
+ @default[:exceptions].map! {|item| item = (item.class == String)? Object.const_get(item) : item }
41
+ end
42
+
43
+ end
44
+
45
+ class ConfigMapper
46
+
47
+ include Sc4ry::Constants
48
+ attr_reader :config
49
+
50
+ def initialize(definition: DEFAULT_CONFIG)
51
+ @config = definition
52
+ @config.each do |key,value|
53
+ self.define_singleton_method "#{key.to_s}=".to_sym do |val|
54
+ key = __method__.to_s.chop.to_sym
55
+ @config[key]=val
56
+ end
57
+ self.define_singleton_method key do
58
+ return @config[__method__]
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,43 @@
1
+ module Sc4ry
2
+ module Constants
3
+
4
+ CURRENT_NOTIFIERS = [:prometheus, :mattermost]
5
+ DEFAULT_CONFIG = {
6
+ :max_failure_count => 5,
7
+ :timeout_value => 20,
8
+ :timeout => false,
9
+ :max_timeout_count => 5,
10
+ :max_time => 10,
11
+ :max_overtime_count => 3,
12
+ :check_delay => 30,
13
+ :notifiers => [],
14
+ :forward_unknown_exceptions => true,
15
+ :raise_on_opening => false,
16
+ :exceptions => [StandardError, RuntimeError]
17
+ }
18
+
19
+ DEFAULT_CONFIG_FORMATS = {
20
+ :max_failure_count => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
21
+ :timeout_value => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
22
+ :timeout => {:proc => Proc::new {|item| [true,false].include? item}, :desc => "must be a Boolean"},
23
+ :max_timeout_count => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
24
+ :max_time => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
25
+ :max_overtime_count => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
26
+ :check_delay => {:proc => Proc::new {|item| item.class == Integer}, :desc => "must be an Integer"},
27
+ :notifiers => {
28
+ :proc => Proc::new {|item|
29
+ item.class == Array and item.select {|val| val.class == Symbol }.size == item.size
30
+ },
31
+ :desc => "must be an Array of Symbol",
32
+ :list => CURRENT_NOTIFIERS},
33
+ :forward_unknown_exceptions => {:proc => Proc::new {|item| [true,false].include? item}, :desc => "must be a Boolean"},
34
+ :raise_on_opening => {:proc => Proc::new {|item| [true,false].include? item}, :desc => "must be a Boolean"},
35
+ :exceptions => {
36
+ :proc => Proc::new {|item| item.class == Array and item.select {|val|
37
+ [Class,String].include? val.class}.size == item.size
38
+ },
39
+ :desc => "must be an Array of Exception(Class) or String"}
40
+ }
41
+
42
+ end
43
+ end
@@ -17,9 +17,13 @@ require 'json'
17
17
  require_relative 'helpers'
18
18
  require_relative 'exceptions'
19
19
  require_relative 'logger'
20
+ require_relative 'constants'
21
+ require_relative 'config'
22
+ require_relative 'notifiers/init'
23
+ require_relative 'exporters/init'
20
24
  require_relative 'backends/init'
25
+
21
26
  require_relative 'store'
22
27
  require_relative 'run_controller'
23
28
  require_relative 'circuits'
24
- require_relative 'notifiers/init'
25
- require_relative 'exporters/init'
29
+
@@ -7,5 +7,19 @@ module Sc4ry
7
7
 
8
8
  end
9
9
 
10
+ class Sc4ryGenericError < StandardError
11
+ def initialize(msg)
12
+ super(msg)
13
+ end
14
+
15
+ end
16
+
17
+ class ConfigError < StandardError
18
+ def initialize(msg)
19
+ super(msg)
20
+ end
21
+
22
+ end
23
+
10
24
  end
11
25
  end
@@ -2,9 +2,12 @@ Dir[File.dirname(__FILE__) + '/*.rb'].sort.each { |file| require file unless Fil
2
2
 
3
3
  module Sc4ry
4
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
- }
5
+
6
+ DEFAULT_NOTIFIERS = {:prometheus => {:class => Sc4ry::Notifiers::Prometheus, :config => {:url => 'http://localhost:9091'}},
7
+ :mattermost => {:class => Sc4ry::Notifiers::Mattermost, :config => {:url => 'http://localhost:9999', :token => "<CHANGE_ME>"}}
8
+ }
9
+ @@notifiers_list = DEFAULT_NOTIFIERS.dup
10
+
8
11
 
9
12
  def Notifiers.list
10
13
  return @@notifiers_list.keys
@@ -10,24 +10,27 @@ module Sc4ry
10
10
  status = options[:config][:status][:general]
11
11
  circuit = options[:circuit]
12
12
  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)
13
+ begin
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)
27
+ end
28
+
29
+ else
30
+ Sc4ry::Helpers.log level: :warn, message: "Mattermost Notifier : can't notify Mattermost not reachable."
27
31
  end
28
-
29
- else
30
- Sc4ry::Helpers.log level: :warn, message: "Mattermost Notifier : can't notify Mattermost not reachable."
32
+ rescue URI::InvalidURIError
33
+ Sc4ry::Helpers.log level: :warn, message: "Mattermost Notifier : URL malformed"
31
34
  end
32
35
  end
33
36
  end
data/lib/sc4ry/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sc4ry
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.8"
3
3
  end
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.7
4
+ version: 0.1.8
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-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: prometheus-client
@@ -60,6 +60,8 @@ files:
60
60
  - lib/sc4ry/backends/memory.rb
61
61
  - lib/sc4ry/backends/redis.rb
62
62
  - lib/sc4ry/circuits.rb
63
+ - lib/sc4ry/config.rb
64
+ - lib/sc4ry/constants.rb
63
65
  - lib/sc4ry/dependencies.rb
64
66
  - lib/sc4ry/exceptions.rb
65
67
  - lib/sc4ry/exporters/init.rb
@@ -76,7 +78,7 @@ homepage: https://github.com/Ultragreen/sc4ry
76
78
  licenses:
77
79
  - MIT
78
80
  metadata: {}
79
- post_install_message:
81
+ post_install_message:
80
82
  rdoc_options: []
81
83
  require_paths:
82
84
  - lib
@@ -91,8 +93,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
93
  - !ruby/object:Gem::Version
92
94
  version: '0'
93
95
  requirements: []
94
- rubygems_version: 3.1.2
95
- signing_key:
96
+ rubygems_version: 3.2.3
97
+ signing_key:
96
98
  specification_version: 4
97
99
  summary: Sc4Ry is Simple Circuitbreaker 4 RubY
98
100
  test_files: []