sc4ry 0.1.7 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
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: []