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 +4 -4
- data/README.md +46 -21
- data/lib/sc4ry/circuits.rb +43 -26
- data/lib/sc4ry/config.rb +65 -0
- data/lib/sc4ry/constants.rb +43 -0
- data/lib/sc4ry/dependencies.rb +6 -2
- data/lib/sc4ry/exceptions.rb +14 -0
- data/lib/sc4ry/notifiers/init.rb +6 -3
- data/lib/sc4ry/notifiers/mattermost.rb +20 -17
- data/lib/sc4ry/version.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 19791a7d72756fe96464fd43fd648010a71ff7491b96702da23c8828c19f58a2
|
4
|
+
data.tar.gz: 6e1d7318a3de28fd0950213f9e8bf344819dc5858f66f5ce0ec5b1eb7275b99f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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 = :
|
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
|
-
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
60
|
-
#
|
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
|
-
|
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://<
|
98
|
+
puts RestClient.get('http://<URL_OF_AN_ENDPOINT>')
|
74
99
|
end
|
75
100
|
end
|
76
101
|
|
data/lib/sc4ry/circuits.rb
CHANGED
@@ -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 =
|
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.
|
24
|
-
|
25
|
-
|
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(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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: :
|
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
|
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
|
data/lib/sc4ry/config.rb
ADDED
@@ -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
|
data/lib/sc4ry/dependencies.rb
CHANGED
@@ -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
|
-
|
25
|
-
require_relative 'exporters/init'
|
29
|
+
|
data/lib/sc4ry/exceptions.rb
CHANGED
data/lib/sc4ry/notifiers/init.rb
CHANGED
@@ -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
|
-
|
6
|
-
|
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
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
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
|
-
|
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
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.
|
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-
|
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.
|
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: []
|