sc4ry 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,11 @@
1
+ # rubygems depends
1
2
  require 'rest-client'
2
3
  require 'prometheus/client'
3
4
  require 'prometheus/client/push'
5
+ require 'redis'
6
+ require 'version'
4
7
 
8
+ # Stdlibs depends
5
9
  require 'logger'
6
10
  require 'timeout'
7
11
  require 'forwardable'
@@ -11,9 +15,10 @@ require 'openssl'
11
15
  require 'net/http'
12
16
  require 'uri'
13
17
  require 'json'
18
+ require 'yaml'
14
19
 
15
20
 
16
-
21
+ # Sc4ry internal depends
17
22
  require_relative 'helpers'
18
23
  require_relative 'exceptions'
19
24
  require_relative 'logger'
@@ -1,5 +1,12 @@
1
+ # Sc4ry module
2
+ # @note namespace
1
3
  module Sc4ry
4
+
5
+ # Sc4ry::Exceptions module
6
+ # @note namespace
2
7
  module Exceptions
8
+
9
+ # Exception use in {Sc4ry::Circuits} when running circuit {Sc4ry::Circuits::run}
3
10
  class CircuitBreaked < StandardError
4
11
  def initialize(msg="Circuit just opened")
5
12
  super(msg)
@@ -7,15 +14,33 @@ module Sc4ry
7
14
 
8
15
  end
9
16
 
17
+ # Generic Exception use in {Sc4ry::Circuits}
10
18
  class Sc4ryGenericError < StandardError
11
- def initialize(msg)
19
+ def initialize(msg="")
20
+ super(msg)
21
+ end
22
+
23
+ end
24
+
25
+ # Exception use in {Sc4ry::Store} or/and {Sc4ry::Backend} on data string issues
26
+ class Sc4ryBackendError < StandardError
27
+ def initialize(msg="")
12
28
  super(msg)
13
29
  end
14
30
 
15
31
  end
16
32
 
33
+ # Exception use in {Sc4ry::Notifiers} on notification issues
34
+ class Sc4ryNotifierError < StandardError
35
+ def initialize(msg="")
36
+ super(msg)
37
+ end
38
+
39
+ end
40
+
41
+ # Exception use in {Sc4ry::Circuits} on config management issues
17
42
  class ConfigError < StandardError
18
- def initialize(msg)
43
+ def initialize(msg="")
19
44
  super(msg)
20
45
  end
21
46
 
data/lib/sc4ry/helpers.rb CHANGED
@@ -1,10 +1,22 @@
1
-
1
+ # Sc4ry module
2
+ # @note namespace
2
3
  module Sc4ry
3
- module Helpers
4
4
 
5
- def Helpers.log(options)
6
- Sc4ry::Logger.current = options[:target] if options[:target]
7
- Sc4ry::Logger.get.send options[:level], "Sc4ry : #{options[:message]}"
5
+ # Sc4ry::Helpers module
6
+ # @note namespace
7
+ module Helpers
8
+
9
+ # class method (module) to help logging messages
10
+ # @param [Symbol] target a specific logger, restored old after
11
+ # @param [Symbol] level (default :info) a logging level (see Logger Stdlib)
12
+ # @param [String] message your message
13
+ # @return [Boolean]
14
+ def Helpers.log(target: nil, level: :info, message: )
15
+ save = Sc4ry::Loggers.current
16
+ Sc4ry::Loggers.current = target if target
17
+ Sc4ry::Loggers.get.send level, "Sc4ry : #{message}"
18
+ Sc4ry::Loggers.current = save
19
+ return true
8
20
  end
9
21
 
10
22
  # TCP/IP service checker
@@ -37,7 +49,9 @@ module Sc4ry
37
49
  end
38
50
  end
39
51
 
40
-
52
+ # class method (module) to help send notifiesby Sc4ry::Notifiers
53
+ # @param [Hash] options a Notifying structure
54
+ # @return [Boolean]
41
55
  def Helpers.notify(options = {})
42
56
  Sc4ry::Notifiers.list.each do |record|
43
57
  notifier = Sc4ry::Notifiers.get name: record
data/lib/sc4ry/logger.rb CHANGED
@@ -1,29 +1,65 @@
1
-
1
+ # Sc4ry module
2
+ # @note namespace
2
3
  module Sc4ry
3
- class Logger
4
+
5
+ # Sc4ry loggers Factory/provider
6
+ # @note must be accessed by [Sc4ry::Circuits.loggers]
7
+ class Loggers
4
8
 
5
9
  @@loggers = {:stdout => ::Logger.new(STDOUT)}
6
10
  @@current = :stdout
7
11
 
8
- def Logger.list_avaible
9
- return @@loggers
12
+ # give the list of available loggers (initially internal Sc4ry logger )
13
+ # @return [Array] of [symbol] the list of defined loggers
14
+ # @note default :stdout => ::Logger(STDOUT) from Ruby Stdlib
15
+ # @example usage
16
+ # include Sc4ry
17
+ # Circuits.loggers.list_available.each {|logger| puts logger }
18
+ def Loggers.list_available
19
+ return @@loggers.keys
10
20
  end
11
21
 
12
- def Logger.current
22
+ # return the current logger name (initially :stdtout )
23
+ # @return [symbol] the name of the logger
24
+ # @example usage
25
+ # include Sc4ry
26
+ # puts Circuits.loggers.current
27
+ def Loggers.current
13
28
  return @@current
14
29
  end
15
30
 
16
- def Logger.get
31
+ # return the current logger Object (initially internal Sc4ry Stdlib Logger on STDOUT )
32
+ # @return [symbol] the name of the logger
33
+ # @example usage
34
+ # include Sc4ry
35
+ # Circuits.loggers.get :stdout
36
+ def Loggers.get
17
37
  return @@loggers[@@current]
18
38
  end
19
39
 
20
- def Logger.current=(sym)
40
+ # Set the current logger
41
+ # @param [Symbol] sym the name of the logger
42
+ # @return [symbol] the name of the logger updated
43
+ # @example usage
44
+ # include Sc4ry
45
+ # Circuits.loggers.current = :newlogger
46
+ def Loggers.current=(sym)
21
47
  raise "Logger not define : #{sym}" unless @@loggers.keys.include? sym
22
48
  @@current = sym
49
+ return @@current
23
50
  end
24
51
 
25
- def Logger.register(options = {})
26
- @@loggers[options[:name]] = options[:instance]
52
+ # register un new logger
53
+ # @param [Symbol] name the name of the new logger
54
+ # @param [Object] instance the new logger object
55
+ # raise Sc4ry::Exceptions::Sc4ryGenericError if name is not a Symbol
56
+ # @example usage
57
+ # include Sc4ry
58
+ # Circuits.loggers.register name: :newlogger, instance: Logger::new('/path/to/my.log')
59
+ def Loggers.register(name: , instance: )
60
+ raise Sc4ry::Exceptions::Sc4ryGenericError, "name: keyword must be a Symbol" unless name.class == Symbol
61
+ @@loggers[name] = instance
62
+ return name
27
63
  end
28
64
 
29
65
  end
@@ -1,32 +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
6
 
7
+ # Sc4ry::Notifiers module
8
+ # @note namespace
9
+ module Notifiers
10
+
11
+ # default notifiers specifications
6
12
  DEFAULT_NOTIFIERS = {:prometheus => {:class => Sc4ry::Notifiers::Prometheus, :config => {:url => 'http://localhost:9091'}},
7
13
  :mattermost => {:class => Sc4ry::Notifiers::Mattermost, :config => {:url => 'http://localhost:9999', :token => "<CHANGE_ME>"}}
8
14
  }
9
15
  @@notifiers_list = DEFAULT_NOTIFIERS.dup
10
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
11
24
 
25
+ # class method how return the list of known notifiers
26
+ # @return [Array] a list of [Symbol] notifiers name
12
27
  def Notifiers.list
13
28
  return @@notifiers_list.keys
14
29
  end
15
30
 
16
- def Notifiers.get(options ={})
17
- 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]
18
36
  end
19
37
 
20
- def Notifiers.register(options)
21
- raise ":name is mandatory" unless options[:name]
22
- raise ":definition is mandatory" unless options[:definition]
23
- @@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
24
44
  end
25
45
 
26
- def Notifiers.config(options)
27
- raise ":name is mandatory" unless options[:name]
28
- raise ":config is mandatory" unless options[:config]
29
- @@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
30
54
  end
31
55
  end
32
56
  end
@@ -1,12 +1,17 @@
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}
@@ -20,7 +25,7 @@ module Sc4ry
20
25
  use_ssl: uri.scheme == "https",
21
26
  }
22
27
  payload = { "text" => "message : #{message } from #{Socket.gethostname}", "username" => "Sc4ry" }
23
- Sc4ry::Helpers.log level: :debug, message: "Mattermost Notifier : #{message}"
28
+ Sc4ry::Helpers.log level: :debug, message: "Mattermost Notifying : #{message}"
24
29
  request.body = ::JSON.dump(payload)
25
30
  response = ::Net::HTTP.start(uri.hostname, uri.port, req_options) do |http|
26
31
  http.request(request)
@@ -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.8"
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
+