chequeo 0.2.0.beta

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 170c778706448e9be5cb4c364a0e74a2daeb35cb
4
+ data.tar.gz: 469cd91e6a037e35287a4884d11f04c67f3f21ef
5
+ SHA512:
6
+ metadata.gz: a87218a7eae2d41ec696676115a930a932cc49d3938d114f93113e9f1f8af49c5a0f0c3e7dd0936c205c8fabab3d0e941b21115aabbc411973653b6c4e26139a
7
+ data.tar.gz: 27e7c79fdfd7b9025f2a832c890c685c55afbeb6cf9cb83975e0724c4ec69603395864ac4523466c96b33e9a5086fc22128b73d0ea634253f01cd41f580e2871
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .idea
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ ## 0.2.0.beta
2
+ - Beta Release of application. Expect breaking changes in the future.
3
+
4
+ ## 0.1.0
5
+ - Create simple daemon
6
+
7
+ ## 0.0.1
8
+ - Initial version created
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem 'simplecov', :require => false, :group => :test
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Jonathan De Jong
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ # chequeo
2
+
3
+ Chequeo provides a framework for running checkups on your application. The goal of checkups is to detect problems in your application and take actions to alert or repair the issue.
4
+
5
+ This could be sending an alert to Slack, a text message, hitting a webhook URL or logging out the alert to a database.
6
+
7
+ ## Inspiration
8
+
9
+ After struggeling internally to deal with building health checks into our system and hearing a wonderful talk at RailsConf 2018 by Ryan Laughlin ([Video](http://confreaks.tv/videos/railsconf2018-the-doctor-is-in-using-checkups-to-find-bugs-in-production)) I decided it would be a good idea to build a library to make checkups easier.
10
+
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'galactic-senate'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ $ bundle
23
+
24
+ Or install it yourself as:
25
+
26
+ $ gem install galactic-senate
27
+
28
+ Generate a config file into your initializers directory.
29
+
30
+ $ rails generate chequeo:install
31
+
32
+ ### Processor
33
+ In order to run the checkups we need a processor. Currently we have a standalone daemon that manages the jobs.
34
+
35
+ #### Standalone
36
+
37
+
38
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/bin/chequeo ADDED
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Quiet some warnings we see when running in warning mode:
4
+ # RUBYOPT=-w bundle exec sidekiq
5
+ $TESTING = false
6
+
7
+ require_relative '../lib/chequeo/cli'
8
+
9
+ begin
10
+ cli = Chequeo::CLI.instance
11
+
12
+ cli.setup
13
+ cli.start
14
+ rescue => e
15
+ raise e if $DEBUG
16
+ STDERR.puts e.message
17
+ STDERR.puts e.backtrace.join("\n")
18
+ exit 1
19
+ end
data/chequeo.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "chequeo/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "chequeo"
8
+ spec.version = Chequeo::VERSION
9
+ spec.authors = ["jdejong"]
10
+ spec.email = [""]
11
+
12
+ spec.summary = 'Checkups Made Easy'
13
+ spec.description = 'A framework to make running checkups on your platform easier.'
14
+ spec.homepage = 'https://github.com/jdejong/chequeo'
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
18
+ f.match(%r{^(test|spec|features)/})
19
+ end
20
+
21
+ spec.executables = ['chequeo']
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.required_ruby_version = '>= 2.2.0'
25
+
26
+ spec.add_runtime_dependency 'twilio-ruby', '>= 5.0', '< 6.0'
27
+ spec.add_runtime_dependency 'slack-ruby-client', '>= 0.9.0'
28
+ spec.add_runtime_dependency 'fugit', '>= 1.1.5'
29
+ spec.add_runtime_dependency 'concurrent-ruby', ">= 1.0"
30
+ spec.add_runtime_dependency 'galactic-senate', '~> 0.1'
31
+ spec.add_runtime_dependency 'oj', '>= 3.6', '< 4'
32
+ spec.add_runtime_dependency 'redis', '>= 4.0'
33
+
34
+ spec.add_development_dependency 'rake', '~> 10'
35
+ spec.add_development_dependency 'minitest', '~> 5.3'
36
+ spec.add_development_dependency "bundler", "~> 1.15"
37
+ end
data/init.rb ADDED
File without changes
data/lib/chequeo.rb ADDED
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+
5
+ class << self
6
+ attr_accessor :config
7
+ end
8
+
9
+ def self.config
10
+ @config ||= Chequeo::Configuration.new
11
+ end
12
+
13
+ def self.reset
14
+ @config = Chequeo::Configuration.new
15
+ end
16
+
17
+ def self.configure
18
+ yield(config)
19
+ end
20
+ end
21
+
22
+ require 'chequeo/configuration'
23
+
24
+ require 'chequeo/processor'
25
+ require 'chequeo/scheduler'
26
+ require 'chequeo/checkup_processor'
27
+ require 'chequeo/scheduled_job'
28
+ require 'chequeo/manager'
29
+
30
+ require 'chequeo/healthchecks/base'
31
+ require 'chequeo/healthchecks/test_check'
32
+ require 'chequeo/healthchecks/system_up'
33
+
34
+ require 'chequeo/notifications/base'
35
+ require 'chequeo/notifications/slack'
36
+ require 'chequeo/notifications/twilio'
37
+ require 'chequeo/notifications/webhook'
38
+ require 'chequeo/notifications/logger'
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ class CheckupProcessor < Chequeo::Processor
5
+
6
+ def process_task
7
+ while !@completed
8
+
9
+ _job_to_process = Chequeo.config.redis.lpop("chequeo-job-queue")
10
+ process_one _job_to_process if _job_to_process
11
+
12
+ sleep 0.1 if !_job_to_process
13
+ end
14
+ end
15
+
16
+ def process_one( job )
17
+ _job = Chequeo::HealthChecks::Base.deserialize(job)
18
+
19
+ _job.process
20
+ _job.notify
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ $stdout.sync = true
4
+
5
+ require 'singleton'
6
+ require 'optparse'
7
+
8
+ module Chequeo
9
+ class CLI
10
+
11
+ include Singleton
12
+
13
+ DEFAULTS = {
14
+
15
+ }
16
+
17
+ SIGNAL_HANDLERS = {
18
+ # Ctrl-C in terminal
19
+ 'INT' => ->(cli) { raise Interrupt },
20
+ # TERM is the signal that the daemon must exit.
21
+ 'TERM' => ->(cli) { raise Interrupt },
22
+ 'USR1' => ->(cli) {
23
+ Rails.logger.info "Received USR1, no longer accepting requests"
24
+ cli.launcher.quiet
25
+ },
26
+ 'TSTP' => ->(cli) {
27
+ Rails.logger.info "Received TSTP, no longer accepting requests"
28
+ cli.launcher.quiet
29
+ },
30
+ 'USR2' => ->(cli) {
31
+
32
+ },
33
+ 'TTIN' => ->(cli) {
34
+ Thread.list.each do |thread|
35
+ Rails.logger.warn "Thread TID-#{(thread.object_id ^ ::Process.pid).to_s(36)}"
36
+ if thread.backtrace
37
+ Rails.logger.warn thread.backtrace.join("\n")
38
+ else
39
+ Rails.logger.warn "<no backtrace available>"
40
+ end
41
+ end
42
+ },
43
+ }
44
+
45
+ def self.options
46
+ @options ||= DEFAULTS.dup
47
+ end
48
+
49
+ def self.options=(opts)
50
+ @options = opts
51
+ end
52
+
53
+ def options
54
+ Chequeo::CLI.options
55
+ end
56
+
57
+ def setup(args=ARGV)
58
+ setup_options(args)
59
+ require_system
60
+
61
+ write_pid
62
+ end
63
+
64
+ def start
65
+ Chequeo.config.logger.debug "Starting Daemon"
66
+
67
+ self_read, self_write = IO.pipe
68
+ sigs = %w(INT TERM TTIN TSTP USR1 USR2)
69
+
70
+ sigs.each do |sig|
71
+ begin
72
+ trap sig do
73
+ self_write.write("#{sig}\n")
74
+ end
75
+ rescue ArgumentError
76
+ puts "Signal #{sig} not supported"
77
+ end
78
+ end
79
+
80
+ begin
81
+
82
+ #spwan scheduler and worker pools
83
+
84
+ Chequeo.config.logger.debug "Starting Schedule Process"
85
+ Chequeo::Scheduler.new(nil).process
86
+ Chequeo.config.logger.debug "Done Starting Schedule Process"
87
+
88
+ Chequeo::Manager.new.process
89
+
90
+ while readable_io = IO.select([self_read])
91
+ signal = readable_io.first[0].gets.strip
92
+ puts "*** SIGNAL RECEIVED :: #{signal} ***"
93
+ handle_signal(signal)
94
+ end
95
+ rescue Interrupt
96
+ Rails.logger.info 'Shutting down'
97
+ #launcher.stop
98
+
99
+ Rails.logger.info "Bye!"
100
+ exit(0)
101
+ end
102
+
103
+ end
104
+
105
+ def handle_signal(sig)
106
+ Rails.logger.debug "Got #{sig} signal"
107
+ handy = SIGNAL_HANDLERS[sig]
108
+ if handy
109
+ handy.call(self)
110
+ else
111
+ Rails.logger.info { "No signal handler for #{sig}" }
112
+ end
113
+ end
114
+
115
+ private
116
+
117
+ def setup_options(args)
118
+ opts = parse_options(args)
119
+
120
+
121
+ options.merge!(opts)
122
+ end
123
+
124
+ def parse_options(argv)
125
+ opts = {}
126
+
127
+ argv.options do |args|
128
+ args.on '-c', '--concurrency INT', "processor threads to use" do |arg|
129
+ opts[:concurrency] = Integer(arg)
130
+ end
131
+
132
+ args.on '-d', '--daemon', "Daemonize process" do |arg|
133
+ opts[:daemon] = arg
134
+ end
135
+
136
+ args.on '-e', '--environment ENV', "Application environment" do |arg|
137
+ opts[:environment] = arg
138
+ end
139
+
140
+ args.on "-v", "--verbose", "Print more verbose output" do |arg|
141
+ opts[:verbose] = arg
142
+ end
143
+
144
+ args.on '-C', '--config PATH', "path to YAML config file" do |arg|
145
+ opts[:config_file] = arg
146
+ end
147
+
148
+ args.on '-L', '--logfile PATH', "path to writable logfile" do |arg|
149
+ opts[:logfile] = arg
150
+ end
151
+
152
+ args.on '-P', '--pidfile PATH', "path to pidfile" do |arg|
153
+ opts[:pidfile] = arg
154
+ end
155
+
156
+ args.on '-V', '--version', "Print version and exit" do |arg|
157
+ puts "Chequeo #{Chequeo::VERSION}"
158
+ exit(0)
159
+ end
160
+
161
+ args.parse!
162
+ end
163
+
164
+ opts
165
+ end
166
+
167
+
168
+ def require_system
169
+ ENV['RACK_ENV'] = ENV['RAILS_ENV']
170
+
171
+ require 'rails'
172
+
173
+ require File.expand_path("./config/application.rb")
174
+ require File.expand_path("./config/environment.rb")
175
+
176
+ end
177
+
178
+ def write_pid
179
+ if path = options[:pidfile]
180
+ pidfile = File.expand_path(path)
181
+ File.open(pidfile, 'w') do |f|
182
+ f.puts ::Process.pid
183
+ end
184
+ end
185
+ end
186
+
187
+ end
188
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ class Configuration
5
+
6
+ attr_accessor :test, :notifications, :schedules, :logger, :redis, :workers, :dead_man_switch
7
+
8
+
9
+ def initialize
10
+ @test = nil
11
+ @notifications = []
12
+ @schedules = []
13
+ @logger = Logger.new(STDOUT)
14
+ @logger.level = Logger::WARN
15
+ @redis = nil
16
+ @workers ||= 5
17
+
18
+ @schedules << Chequeo::ScheduledJob.new("*/5 * * * *", Chequeo::HealthChecks::SystemUp, {})
19
+ end
20
+
21
+
22
+ def schedule(crontab, checkup_class, options = {})
23
+ Chequeo.config.logger.debug "Chequeo::Configuration.schedule - Loading #{checkup_class} to run on schedule #{crontab} with options #{options}"
24
+ @schedules << Chequeo::ScheduledJob.new(crontab, checkup_class, options)
25
+ end
26
+
27
+ def add_notification(notification_class, rules = {}, configuration = {}, options = {})
28
+ Chequeo.config.logger.debug "Chequeo::Configuration.add_notification - Adding #{notification_class} adding rules #{rules} and configuration #{configuration} with options #{options}"
29
+ notification = notification_class.new(options)
30
+ notification.configure(rules, configuration)
31
+ @notifications << notification
32
+ end
33
+
34
+ def dead_mans_switch(&block)
35
+ @dead_man_switch = block
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oj'
4
+
5
+ module Chequeo
6
+ module HealthChecks
7
+ class Base
8
+
9
+ attr_accessor :jid, :enqueue_time, :type, :errors, :warnings, :completion_text, :rules
10
+
11
+ def initialize(options = {})
12
+ @enqueue_time = Time.now.to_i
13
+ @jid = Digest::SHA1.hexdigest("#{self.class.name}:#{options.to_s}:#{@enqueue_time}")
14
+ @type = self.class.name
15
+ @errors = []
16
+ @warnings = []
17
+ @completion_text = nil
18
+ @rules = options.has_key?(:rules) ? options[:rules] : {}
19
+ end
20
+
21
+ def process
22
+ return
23
+ end
24
+
25
+ def notify
26
+ Chequeo.config.notifications.each do |notification|
27
+ notification.send_notifications(self)
28
+ end
29
+ end
30
+
31
+ def get_job_title
32
+ "Chequeo Job ##{@jid} - #{self.class.name.demodulize}"
33
+ end
34
+
35
+ def get_short_job_title
36
+ "[Chequeo] [#{self.class.name.demodulize}] ##{@jid} - "
37
+ end
38
+
39
+ def get_text
40
+ val = "Chequeo Job ##{@jid} for type #{self.class.name.demodulize} completed"
41
+ val += "\n\n#{@completion_text}" if @completion_text
42
+ val
43
+ end
44
+
45
+ def serialize
46
+ Oj.dump(self)
47
+ end
48
+
49
+ def self.deserialize(data)
50
+ parsed_obj = Oj.load(data)
51
+ parsed_obj
52
+ end
53
+
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ module HealthChecks
5
+ class ModelValidity < Base
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+
2
+ # frozen_string_literal: true
3
+
4
+ module Chequeo
5
+ module HealthChecks
6
+ class SystemUp < Base
7
+
8
+ def process
9
+ Chequeo.config.dead_man_switch.call if Chequeo.config.dead_man_switch
10
+ end
11
+
12
+ def notify
13
+ return #do not notify since we are going to hit the endpoint for checkins
14
+ end
15
+
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ module HealthChecks
5
+ class TestCheck < Base
6
+
7
+
8
+ def process
9
+ Chequeo.config.logger.debug "This is a test, this is only a test."
10
+ return
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ class Manager
5
+
6
+ attr_reader :workers
7
+
8
+ def initialize
9
+ Chequeo.config.logger.debug "Creating Worker Pool"
10
+ @workers ||= []
11
+
12
+ Chequeo.config.workers.times do
13
+ @workers << Chequeo::CheckupProcessor.new
14
+ end
15
+ Chequeo.config.logger.debug "Creating Worker Pool"
16
+ end
17
+
18
+ def process
19
+ Chequeo.config.logger.debug "Starting Worker Pool"
20
+ @workers.each do |w|
21
+ w.process
22
+ end
23
+ Chequeo.config.logger.debug "Starting Worker Pool"
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ module Notifications
5
+ class Base
6
+
7
+ attr_accessor :rules, :configuration
8
+
9
+ def initialize(options = {})
10
+ @rules = {:on_completion => true, :warning_threshold => 10, :error_threshold => 10}
11
+ @configuration = {}
12
+ end
13
+
14
+ def configure(rules = {}, config = {})
15
+ @rules.merge!(rules)
16
+ @configuration.merge!(config)
17
+ end
18
+
19
+ def valid?
20
+ true
21
+ end
22
+
23
+ def send_notifications( job , job_rules = {})
24
+ return unless valid?
25
+ _rules = @rules.merge(job_rules) #merge in job specific overrides
26
+ send_warnings(job) if job.warnings.count > 0 && job.warnings.count >= ( _rules[:warning_threshold] || 0 )
27
+ send_errors(job) if job.errors.count > 0 && job.errors.count >= ( _rules[:error_threshold] || 0 )
28
+ send_on_completion(job) if _rules[:on_completion]
29
+ end
30
+
31
+ def send_warnings
32
+ return
33
+ end
34
+
35
+ def send_errors
36
+ return
37
+ end
38
+
39
+ def send_on_completion
40
+ return
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chequeo/notifications/base'
4
+
5
+ module Chequeo
6
+ module Notifications
7
+ class Logger < Base
8
+
9
+ #
10
+ # configuration: {
11
+ # logger: LOGGER_INSTANCE
12
+ # }
13
+
14
+ def valid?
15
+ @configuration && @configuration.has_key?(:logger)
16
+ end
17
+
18
+ def send_on_completion(job)
19
+ _text = job.get_text
20
+ send_logger_notice(_text)
21
+ end
22
+
23
+ def send_warnings(job)
24
+ job.warnings.each{|x| send_logger_warning("#{job.get_short_job_title}#{x}") }
25
+ end
26
+
27
+ def send_errors(job)
28
+ job.errors.each{|x| send_logger_error("#{job.get_short_job_title}#{x}") }
29
+ end
30
+
31
+ def send_logger_warning(text)
32
+ begin
33
+ @configuration[:logger].warn(text)
34
+ rescue => e
35
+ Chequeo.config.logger.error "Chequeo::Notifications::Logger.send_notifications - #{e.message}"
36
+ Chequeo.config.logger.error "Chequeo::Notifications::Logger.send_notifications - #{e.backtrace.inspect}"
37
+ end
38
+ end
39
+
40
+ def send_logger_error(text)
41
+ begin
42
+ @configuration[:logger].error(text)
43
+ rescue => e
44
+ Chequeo.config.logger.error "Chequeo::Notifications::Logger.send_notifications - #{e.message}"
45
+ Chequeo.config.logger.error "Chequeo::Notifications::Logger.send_notifications - #{e.backtrace.inspect}"
46
+ end
47
+ end
48
+
49
+ def send_logger_notice(text)
50
+ begin
51
+ @configuration[:logger].info(text)
52
+ rescue => e
53
+ Chequeo.config.logger.error "Chequeo::Notifications::Logger.send_notifications - #{e.message}"
54
+ Chequeo.config.logger.error "Chequeo::Notifications::Logger.send_notifications - #{e.backtrace.inspect}"
55
+ end
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chequeo/notifications/base'
4
+ require 'slack-ruby-client'
5
+
6
+ module Chequeo
7
+ module Notifications
8
+ class Slack < Base
9
+
10
+ #
11
+ # configuration: {
12
+ # token: YOUR_SLACK_API_TOKEN,
13
+ # channel: YOUR_SLACK_CHANNEL_HERE
14
+ # }
15
+
16
+ def valid?
17
+ @configuration && @configuration.has_key?(:token) && @configuration.has_key?(:channel)
18
+ end
19
+
20
+ def send_on_completion(job)
21
+ _title = job.get_job_title
22
+ _text = job.get_text
23
+ send_message(_text, "good", _title)
24
+ end
25
+
26
+ def send_warnings(job)
27
+ _title = job.get_job_title
28
+ _warnings = job.warnings.collect{|x| "- #{x}"}.join("\n")
29
+ send_message(_warnings, "warning", _title)
30
+ end
31
+
32
+ def send_errors(job)
33
+ _title = job.get_job_title
34
+ _errors = job.errors.collect{|x| "- #{x}"}.join("\n")
35
+ send_message(_errors, "danger", _title)
36
+ end
37
+
38
+ def send_message(text, color, title)
39
+ begin
40
+ client = ::Slack::Web::Client.new(token: @configuration[:token])
41
+ client.chat_postMessage(
42
+ channel: "#{@configuration[:channel]}",
43
+ as_user: true,
44
+ attachments: [
45
+ {
46
+ fallback: "Chequeo has posted a notification to slack.",
47
+ title: title,
48
+ text: text,
49
+ color: color
50
+ }
51
+ ]
52
+ )
53
+ rescue => e
54
+ Chequeo.config.logger.error "Chequeo::Notifications::Slack.send_notifications - #{e.message}"
55
+ Chequeo.config.logger.error "Chequeo::Notifications::Slack.send_notifications - #{e.backtrace.inspect}"
56
+ end
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chequeo/notifications/base'
4
+
5
+ module Chequeo
6
+ module Notifications
7
+ class Twilio < Base
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'chequeo/notifications/base'
4
+
5
+ module Chequeo
6
+ module Notifications
7
+ class Webhook < Base
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ class Processor
5
+
6
+ attr_reader :thread
7
+
8
+ def initialize #(mgr)
9
+ Chequeo.config.logger.debug "Start Chequeo::Processor.initialize"
10
+ #@mgr = mgr
11
+ @thread = nil
12
+ @completed = false
13
+ end
14
+
15
+ def process
16
+ @thread ||= Thread.new {
17
+ begin
18
+ process_task
19
+ rescue => e
20
+ Chequeo.config.logger.error "Chequeo::Scheduler.process - #{e.message}"
21
+ Chequeo.config.logger.error "Chequeo::Scheduler.process - #{e.backtrace.inspect}"
22
+ end
23
+ }
24
+ end
25
+
26
+ def process_task
27
+ return
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ class ScheduledJob
5
+
6
+ attr_accessor :cron, :klass, :options, :jid
7
+
8
+ def initialize(cron, klass, options = {})
9
+ @options = options
10
+ @cron = cron
11
+ @klass = klass
12
+ @jid = Digest::SHA1.hexdigest("#{cron.to_s}:#{klass.to_s}:#{options.to_s}")
13
+ end
14
+
15
+ def get_klass
16
+ klass #TODO: Handle if klass is a string
17
+ end
18
+
19
+ def generate_job_run_id job_time
20
+ { job_time: _time, job_id: Digest::SHA1.hexdigest("#{cron.to_s}:#{klass.to_s}:#{options.to_s}:#{job_time}") }
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'galactic-senate'
4
+ require 'fugit'
5
+
6
+ module Chequeo
7
+ class Scheduler < Chequeo::Processor
8
+ def initialize(mgr)
9
+ Chequeo.config.logger.debug "Start Chequeo::Scheduler.initialize"
10
+
11
+ @leader = false
12
+
13
+ GalacticSenate.configure do |config|
14
+ config.redis = Chequeo.config.redis
15
+ config.logger = Chequeo.config.logger
16
+
17
+ config.on(:elected) do
18
+ Rails.logger.warn "I was just elected!!"
19
+ @leader = true
20
+ end
21
+
22
+ config.on(:ousted) do
23
+ Rails.logger.warn "I was just ousted!!"
24
+ @leader = false
25
+ end
26
+ end
27
+
28
+ Chequeo.config.logger.debug "Starting the senate"
29
+
30
+ GalacticSenate::Delegation.instance.debate
31
+
32
+ Chequeo.config.logger.debug "Senate Started"
33
+
34
+ Chequeo.config.redis.del("chequeo-jobs")
35
+
36
+ Chequeo.config.schedules.each do |element|
37
+ Chequeo.config.redis.sadd("chequeo-jobs", element.jid)
38
+ end
39
+
40
+ Chequeo.config.logger.debug "End Chequeo::Scheduler.initialize"
41
+ end
42
+
43
+
44
+
45
+ def process
46
+ Chequeo.config.logger.debug "Start Chequeo::Scheduler.process"
47
+
48
+ timer_task = Concurrent::TimerTask.new(execution_interval: 30) do |task|
49
+
50
+ begin
51
+ process_task
52
+ rescue => e
53
+ Chequeo.config.logger.error "Chequeo::Scheduler.process - #{e.message}"
54
+ Chequeo.config.logger.error "Chequeo::Scheduler.process - #{e.backtrace.inspect}"
55
+ end
56
+ end
57
+
58
+ timer_task.execute
59
+ Chequeo.config.logger.debug "End Chequeo::Scheduler.process"
60
+ end
61
+
62
+ def process_task
63
+ Chequeo.config.logger.debug "@leader = #{@leader}"
64
+ return unless @leader
65
+
66
+ Chequeo.config.schedules.each do |element|
67
+ _tempest_fugit = Fugit::Cron.parse(element.cron)
68
+
69
+ _last_element = Chequeo.config.redis.zrange("chequeo-job-#{element.jid}", -1, -1, :with_scores => true)
70
+ _last_element_score = !_last_element.empty? ? _last_element[0][1] : -1
71
+
72
+ #puts "next = #{_tempest_fugit.next_time.to_i} - previous = #{_tempest_fugit.previous_time.to_i} - last = #{_last_element_score}"
73
+ if _last_element_score <= _tempest_fugit.previous_time.to_i
74
+
75
+ _job = element.klass.new(element.options)
76
+
77
+ Chequeo.config.logger.debug "Chequeo::Scheduler::process_task - Queueing #{_job.jid} of type #{_job.class.name}"
78
+
79
+ Chequeo.config.redis.lpush("chequeo-job-queue", _job.serialize)
80
+ Chequeo.config.redis.zadd("chequeo-job-#{element.jid}", _job.enqueue_time, _job.jid)
81
+ Chequeo.config.redis.zremrangebyrank("chequeo-job-#{element.jid}", 0, -26)
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+
88
+ end
89
+ end
90
+
91
+
92
+
93
+
94
+
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Chequeo
4
+ VERSION = "0.2.0.beta"
5
+ end
@@ -0,0 +1,11 @@
1
+ module Chequeo
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def copy_initializer
7
+ template "initializer.rb", "config/initializers/chequeo.rb"
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ Chequeo.configure do |config|
2
+ # config.logger = Rails.logger
3
+ end
metadata ADDED
@@ -0,0 +1,226 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chequeo
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0.beta
5
+ platform: ruby
6
+ authors:
7
+ - jdejong
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-09-27 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: twilio-ruby
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '6.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '5.0'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '6.0'
33
+ - !ruby/object:Gem::Dependency
34
+ name: slack-ruby-client
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: 0.9.0
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 0.9.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: fugit
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.1.5
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: 1.1.5
61
+ - !ruby/object:Gem::Dependency
62
+ name: concurrent-ruby
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '1.0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '1.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: galactic-senate
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '0.1'
82
+ type: :runtime
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.1'
89
+ - !ruby/object:Gem::Dependency
90
+ name: oj
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '3.6'
96
+ - - "<"
97
+ - !ruby/object:Gem::Version
98
+ version: '4'
99
+ type: :runtime
100
+ prerelease: false
101
+ version_requirements: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '3.6'
106
+ - - "<"
107
+ - !ruby/object:Gem::Version
108
+ version: '4'
109
+ - !ruby/object:Gem::Dependency
110
+ name: redis
111
+ requirement: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '4.0'
116
+ type: :runtime
117
+ prerelease: false
118
+ version_requirements: !ruby/object:Gem::Requirement
119
+ requirements:
120
+ - - ">="
121
+ - !ruby/object:Gem::Version
122
+ version: '4.0'
123
+ - !ruby/object:Gem::Dependency
124
+ name: rake
125
+ requirement: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - "~>"
128
+ - !ruby/object:Gem::Version
129
+ version: '10'
130
+ type: :development
131
+ prerelease: false
132
+ version_requirements: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - "~>"
135
+ - !ruby/object:Gem::Version
136
+ version: '10'
137
+ - !ruby/object:Gem::Dependency
138
+ name: minitest
139
+ requirement: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - "~>"
142
+ - !ruby/object:Gem::Version
143
+ version: '5.3'
144
+ type: :development
145
+ prerelease: false
146
+ version_requirements: !ruby/object:Gem::Requirement
147
+ requirements:
148
+ - - "~>"
149
+ - !ruby/object:Gem::Version
150
+ version: '5.3'
151
+ - !ruby/object:Gem::Dependency
152
+ name: bundler
153
+ requirement: !ruby/object:Gem::Requirement
154
+ requirements:
155
+ - - "~>"
156
+ - !ruby/object:Gem::Version
157
+ version: '1.15'
158
+ type: :development
159
+ prerelease: false
160
+ version_requirements: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - "~>"
163
+ - !ruby/object:Gem::Version
164
+ version: '1.15'
165
+ description: A framework to make running checkups on your platform easier.
166
+ email:
167
+ - ''
168
+ executables:
169
+ - chequeo
170
+ extensions: []
171
+ extra_rdoc_files: []
172
+ files:
173
+ - ".gitignore"
174
+ - CHANGELOG.md
175
+ - Gemfile
176
+ - LICENSE
177
+ - README.md
178
+ - Rakefile
179
+ - bin/chequeo
180
+ - chequeo.gemspec
181
+ - init.rb
182
+ - lib/chequeo.rb
183
+ - lib/chequeo/checkup_processor.rb
184
+ - lib/chequeo/cli.rb
185
+ - lib/chequeo/configuration.rb
186
+ - lib/chequeo/healthchecks/base.rb
187
+ - lib/chequeo/healthchecks/model_validity.rb
188
+ - lib/chequeo/healthchecks/system_up.rb
189
+ - lib/chequeo/healthchecks/test_check.rb
190
+ - lib/chequeo/manager.rb
191
+ - lib/chequeo/notifications/base.rb
192
+ - lib/chequeo/notifications/logger.rb
193
+ - lib/chequeo/notifications/slack.rb
194
+ - lib/chequeo/notifications/twilio.rb
195
+ - lib/chequeo/notifications/webhook.rb
196
+ - lib/chequeo/processor.rb
197
+ - lib/chequeo/scheduled_job.rb
198
+ - lib/chequeo/scheduler.rb
199
+ - lib/chequeo/version.rb
200
+ - lib/generators/chequeo/install_generator.rb
201
+ - lib/generators/chequeo/templates/initializer.rb
202
+ homepage: https://github.com/jdejong/chequeo
203
+ licenses:
204
+ - MIT
205
+ metadata: {}
206
+ post_install_message:
207
+ rdoc_options: []
208
+ require_paths:
209
+ - lib
210
+ required_ruby_version: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - ">="
213
+ - !ruby/object:Gem::Version
214
+ version: 2.2.0
215
+ required_rubygems_version: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - ">"
218
+ - !ruby/object:Gem::Version
219
+ version: 1.3.1
220
+ requirements: []
221
+ rubyforge_project:
222
+ rubygems_version: 2.4.8
223
+ signing_key:
224
+ specification_version: 4
225
+ summary: Checkups Made Easy
226
+ test_files: []