checkup 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *~
2
+ .DS_Store
3
+ .rvmrc
4
+ models/
5
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ checkup (0.0.1)
5
+ httparty
6
+ thor (~> 0.14.6)
7
+
8
+ GEM
9
+ remote: http://rubygems.org/
10
+ specs:
11
+ httparty (0.9.0)
12
+ multi_json (~> 1.0)
13
+ multi_xml
14
+ multi_json (1.3.6)
15
+ multi_xml (0.5.1)
16
+ thor (0.14.6)
17
+
18
+ PLATFORMS
19
+ ruby
20
+
21
+ DEPENDENCIES
22
+ checkup!
data/bin/checkup ADDED
@@ -0,0 +1,9 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ # whole library
4
+ require File.expand_path("../../lib/checkup", __FILE__)
5
+ # cli interface for the binary
6
+ require File.expand_path("../../lib/checkup/cli/utility", __FILE__)
7
+
8
+
9
+ Checkup::CLI::Utility.start
data/checkup.gemspec ADDED
@@ -0,0 +1,24 @@
1
+
2
+
3
+ require File.expand_path('lib/checkup/version')
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'checkup'
7
+ gem.version = Checkup::Version.current
8
+ gem.platform = Gem::Platform::RUBY
9
+ gem.authors = 'Heiko Moeller'
10
+ gem.email = 'femaref@googlemail.com'
11
+ gem.homepage = 'http://rubygems.org/gems/checkup'
12
+ gem.summary = ''
13
+
14
+
15
+ gem.files = %x[git ls-files].split("\n")
16
+ gem.test_files = %x[git ls-files -- {spec}/*].split("\n")
17
+ gem.require_path = 'lib'
18
+
19
+ gem.executables = ['checkup']
20
+
21
+ gem.add_dependency 'thor', ['~> 0.14.6']
22
+ gem.add_dependency 'httparty'
23
+
24
+ end
data/lib/checkup.rb ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+
3
+ begin
4
+ require 'thor'
5
+ rescue LoadError
6
+ puts 'Checkup requires the Thor library'
7
+ exit 1
8
+ end
9
+
10
+ module Checkup
11
+ LIBRARY_PATH = File.join(File.dirname(__FILE__), 'checkup')
12
+ TEMPLATE_PATH = File.join(File.dirname(__FILE__), '..', 'templates')
13
+ CLI_PATH = File.join(LIBRARY_PATH, 'cli')
14
+ CONFIGURATION_PATH = File.join(LIBRARY_PATH, 'configuration')
15
+
16
+ autoload :Binder, File.join(LIBRARY_PATH, 'binder')
17
+ autoload :Config, File.join(LIBRARY_PATH, 'config')
18
+ autoload :Dependency, File.join(LIBRARY_PATH, 'dependency')
19
+ autoload :Errors, File.join(LIBRARY_PATH, 'errors')
20
+ autoload :Logger, File.join(LIBRARY_PATH, 'logger')
21
+ autoload :Model, File.join(LIBRARY_PATH, 'model')
22
+ autoload :Template, File.join(LIBRARY_PATH, 'template')
23
+ autoload :Version, File.join(LIBRARY_PATH, 'version')
24
+
25
+ module CLI
26
+ autoload :Utility, File.join(CLI_PATH, 'utility')
27
+ end
28
+
29
+ module Configuration
30
+ autoload :Base, File.join(CONFIGURATION_PATH, 'base')
31
+ autoload :Helpers, File.join(CONFIGURATION_PATH, 'helpers')
32
+
33
+ module Notifier
34
+ autoload :Base, File.join(CONFIGURATION_PATH, 'notifier', 'base')
35
+ autoload :Mail, File.join(CONFIGURATION_PATH, 'notifier', 'mail')
36
+ end
37
+
38
+ module Service
39
+ autoload :Base, File.join(CONFIGURATION_PATH, 'service', 'base')
40
+ autoload :Http, File.join(CONFIGURATION_PATH, 'service', 'http')
41
+ end
42
+ end
43
+
44
+ module Notifier
45
+ autoload :Base, File.join(LIBRARY_PATH, 'notifier', 'base')
46
+ autoload :Mail, File.join(LIBRARY_PATH, 'notifier', 'mail')
47
+ end
48
+
49
+ module Service
50
+ autoload :Base, File.join(LIBRARY_PATH, 'service', 'base')
51
+ autoload :Http, File.join(LIBRARY_PATH, 'service', 'http')
52
+ end
53
+ end
@@ -0,0 +1,14 @@
1
+ module Checkup
2
+ class Binder
3
+ def initialize(key_and_values)
4
+ key_and_values.each do |key, value|
5
+ instance_variable_set("@#{key}", value)
6
+ end
7
+ end
8
+
9
+ def get_binding
10
+ binding
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,62 @@
1
+ module Checkup
2
+ module CLI
3
+ class Utility < Thor
4
+ include Thor::Actions
5
+
6
+ method_option :trigger, :type => :string, :required => true, :aliases => ['-t', '--triggers']
7
+ method_option :root_path, :type => :string, :default => '', :aliases => '-r'
8
+ desc 'perform', 'executes a named model'
9
+ def perform
10
+ root_path = options[:root_path]
11
+
12
+ if root_path == ""
13
+ root_path = Checkup::Config::DEFAULT_ROOT_PATH
14
+ end
15
+
16
+ load root_path
17
+
18
+ triggers = options[:trigger].split(",")
19
+ triggers.map!(&:strip).map! {|t|
20
+ t.include?('*') ? Model.find_matching(t) : t
21
+ }.flatten!
22
+
23
+ triggers.each do |trigger|
24
+ model = Model.find(trigger)
25
+ model.perform!
26
+ Logger.clear!
27
+ end
28
+ end
29
+
30
+ method_option :root_path, :type => :string, :default => '', :aliases => '-r'
31
+ desc 'list', 'lists all models'
32
+ def list
33
+ root_path = options[:root_path]
34
+
35
+ if root_path == ""
36
+ root_path = Checkup::Config::DEFAULT_ROOT_PATH
37
+ end
38
+
39
+ load root_path
40
+
41
+ Model.all.each do |model|
42
+ puts model.trigger
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def load (path)
49
+ path = File.expand_path path
50
+
51
+ if !Dir.exists? path
52
+ FileUtils.mkdir_p path
53
+ end
54
+
55
+ Dir[File.join(path, "*.rb")].each do |file|
56
+ Checkup::Config.class_eval File.read(File.expand_path(File.basename(file), path))
57
+ end
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,41 @@
1
+ module Checkup
2
+ module Config
3
+ DEFAULT_ROOT_PATH = '~/.checkup'
4
+
5
+ class << self
6
+ def add_dsl_constants!
7
+ create_modules(
8
+ self,
9
+ [
10
+ # Services
11
+ ['Http'],
12
+ # Notifiers
13
+ ['Mail']
14
+ ]
15
+ )
16
+ end
17
+
18
+ def create_modules(scope, names)
19
+ names.flatten.each do |name|
20
+ if name.is_a?(Hash)
21
+ name.each do |key, val|
22
+ create_modules(get_or_create_empty_module(scope, key), [val])
23
+ end
24
+ else
25
+ get_or_create_empty_module(scope, name)
26
+ end
27
+ end
28
+ end
29
+
30
+ def get_or_create_empty_module(scope, const)
31
+ if scope.const_defined?(const)
32
+ scope.const_get(const)
33
+ else
34
+ scope.const_set(const, Module.new)
35
+ end
36
+ end
37
+ end
38
+
39
+ add_dsl_constants!
40
+ end
41
+ end
@@ -0,0 +1,6 @@
1
+ module Checkup
2
+ module Configuration
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,52 @@
1
+ # encoding: utf-8
2
+
3
+ module Checkup
4
+ module Configuration
5
+ module Helpers
6
+
7
+ ##
8
+ # Finds all the object's getter methods and checks the global
9
+ # configuration for these methods, if they respond then they will
10
+ # assign the object's attribute(s) to that particular global configuration's attribute
11
+ def load_defaults!
12
+ module_names = self.class.name.split('::')[1..-1]
13
+ configuration = Checkup::Configuration
14
+ module_names.each do |module_name|
15
+ configuration = configuration.const_get(module_name)
16
+ end
17
+
18
+ getter_methods.each do |attribute|
19
+ if configuration.respond_to?(attribute)
20
+ self.send("#{attribute}=", configuration.send(attribute))
21
+ end
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Clears all the defaults that may have been set by the user
27
+ def clear_defaults!
28
+ setter_methods.each do |method|
29
+ self.send(method, nil)
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ ##
36
+ # Returns an Array of the setter methods (as String)
37
+ def setter_methods
38
+ methods.map do |method|
39
+ method = method.to_s
40
+ method if method =~ /^\w(\w|\d|\_)+\=$/ and method != 'taguri='
41
+ end.compact
42
+ end
43
+
44
+ ##
45
+ # Returns an Array of getter methods (as String)
46
+ def getter_methods
47
+ setter_methods.map {|method| method.sub('=','') }
48
+ end
49
+
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,13 @@
1
+ module Checkup
2
+ module Configuration
3
+ module Notifier
4
+ class Base < Configuration::Base
5
+ class << self
6
+ attr_accessor :on_success
7
+ attr_accessor :on_warning
8
+ attr_accessor :on_failure
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,112 @@
1
+ # encoding: utf-8
2
+
3
+ module Checkup
4
+ module Configuration
5
+ module Notifier
6
+ class Mail < Base
7
+ class << self
8
+
9
+ ##
10
+ # Mail delivery method to be used by the Mail gem.
11
+ # Supported methods:
12
+ #
13
+ # `:smtp` [::Mail::SMTP] (default)
14
+ # : Settings used only by this method:
15
+ # : `address`, `port`, `domain`, `user_name`, `password`
16
+ # : `authentication`, `enable_starttls_auto`, `openssl_verify_mode`
17
+ #
18
+ # `:sendmail` [::Mail::Sendmail]
19
+ # : Settings used only by this method:
20
+ # : `sendmail`, `sendmail_args`
21
+ #
22
+ # `:exim` [::Mail::Exim]
23
+ # : Settings used only by this method:
24
+ # : `exim`, `exim_args`
25
+ #
26
+ # `:file` [::Mail::FileDelivery]
27
+ # : Settings used only by this method:
28
+ # : `mail_folder`
29
+ #
30
+ attr_accessor :delivery_method
31
+
32
+ ##
33
+ # Sender and Receiver email addresses
34
+ # Examples:
35
+ # sender - my.email.address@gmail.com
36
+ # receiver - your.email.address@gmail.com
37
+ attr_accessor :from, :to
38
+
39
+ ##
40
+ # The address to use
41
+ # Example: smtp.gmail.com
42
+ attr_accessor :address
43
+
44
+ ##
45
+ # The port to connect to
46
+ # Example: 587
47
+ attr_accessor :port
48
+
49
+ ##
50
+ # Your domain (if applicable)
51
+ # Example: mydomain.com
52
+ attr_accessor :domain
53
+
54
+ ##
55
+ # Username and Password (sender email's credentials)
56
+ # Examples:
57
+ # user_name - meskyanichi
58
+ # password - my_secret_password
59
+ attr_accessor :user_name, :password
60
+
61
+ ##
62
+ # Authentication type
63
+ # Example: plain
64
+ attr_accessor :authentication
65
+
66
+ ##
67
+ # Automatically set TLS
68
+ # Example: true
69
+ attr_accessor :enable_starttls_auto
70
+
71
+ ##
72
+ # OpenSSL Verify Mode
73
+ # Example: none - Only use this option for a self-signed and/or wildcard certificate
74
+ attr_accessor :openssl_verify_mode
75
+
76
+ ##
77
+ # When using the `:sendmail` `delivery_method` option,
78
+ # this may be used to specify the absolute path to `sendmail` (if needed)
79
+ # Example: '/usr/sbin/sendmail'
80
+ attr_accessor :sendmail
81
+
82
+ ##
83
+ # Optional arguments to pass to `sendmail`
84
+ # Note that this will override the defaults set by the Mail gem (currently: '-i -t')
85
+ # So, if set here, be sure to set all the arguments you require.
86
+ # Example: '-i -t -X/tmp/traffic.log'
87
+ attr_accessor :sendmail_args
88
+
89
+ ##
90
+ # When using the `:exim` `delivery_method` option,
91
+ # this may be used to specify the absolute path to `exim` (if needed)
92
+ # Example: '/usr/sbin/exim'
93
+ attr_accessor :exim
94
+
95
+ ##
96
+ # Optional arguments to pass to `exim`
97
+ # Note that this will override the defaults set by the Mail gem (currently: '-i -t')
98
+ # So, if set here, be sure to set all the arguments you require.
99
+ # Example: '-i -t -X/tmp/traffic.log'
100
+ attr_accessor :exim_args
101
+
102
+ ##
103
+ # Folder where mail will be kept when using the `:file` `delivery_method` option.
104
+ # Default location is '$HOME/backup-mails'
105
+ # Example: '/tmp/test-mails'
106
+ attr_accessor :mail_folder
107
+
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,10 @@
1
+ module Checkup
2
+ module Configuration
3
+ module Service
4
+ class Base < Configuration::Base
5
+ class << self
6
+ end
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ module Checkup
2
+ module Configuration
3
+ module Service
4
+ class Http < Base
5
+ class << self
6
+ attr_accessor :url
7
+ attr_accessor :method
8
+ attr_accessor :expected_code
9
+ attr_accessor :expected_response
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,38 @@
1
+ # encoding: utf-8
2
+
3
+ module Checkup
4
+ # Small dependency loader, so we don't have to require all possible gems in the Gemfile
5
+ class Dependency
6
+ def self.all
7
+ @all ||= {
8
+ 'mail' => {
9
+ :require => 'mail',
10
+ :version => '>= 2.4.0',
11
+ :for => 'Sending Emails (Mail Notifier)'
12
+ },
13
+ 'httparty' => {
14
+ :require => 'httparty',
15
+ :version => '>= 0',
16
+ :for => 'Http up check'
17
+ }
18
+ }
19
+ end
20
+
21
+ def self.load (name)
22
+ begin
23
+ gem(name, all[name][:version])
24
+ require(all[name][:require])
25
+ rescue LoadError
26
+ Logger.error Errors::Dependency::LoadError.new(<<-EOS)
27
+ Dependency missing
28
+ Dependency required for:
29
+ #{all[name][:for]}
30
+ To install the gem, issue the following command:
31
+ > gem install #{name} -v '#{all[name][:version]}'
32
+ Please try again after installing the missing dependency.
33
+ EOS
34
+ exit 1
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,80 @@
1
+ module Checkup
2
+ # when included, automatically creates new Exceptions depending on the Const passed in
3
+ module ErrorsHelper
4
+ def const_missing(const)
5
+ if const.to_s.end_with?('Error')
6
+ module_eval("class #{const} < Checkup::Errors::Error; end")
7
+ else
8
+ module_eval("module #{const}; extend Checkup::ErrorsHelper; end")
9
+ end
10
+ const_get(const)
11
+ end
12
+ end
13
+
14
+ module Errors
15
+ extend ErrorsHelper
16
+
17
+ class Error < StandardError
18
+
19
+ def self.wrap(orig_err, msg = nil)
20
+ new(msg, orig_err)
21
+ end
22
+
23
+ def initialize(msg = nil, orig_err = nil)
24
+ super(msg)
25
+ set_backtrace(orig_err.backtrace) if @orig_err = orig_err
26
+ end
27
+
28
+ def to_s
29
+ return @to_s if @to_s
30
+ orig_to_s = super()
31
+
32
+ if orig_to_s == self.class.to_s
33
+ msg = orig_err_msg ?
34
+ "#{orig_err_class}: #{orig_err_msg}" : orig_err_class
35
+ else
36
+ msg = format_msg(orig_to_s)
37
+ msg << "\n Reason: #{orig_err_class}" + (orig_err_msg ?
38
+ "\n #{orig_err_msg}" : ' (no message given)') if @orig_err
39
+ end
40
+
41
+ @to_s = msg ? msg_prefix + msg : class_name
42
+ end
43
+
44
+ private
45
+
46
+ def msg_prefix
47
+ @msg_prefix ||= class_name + ': '
48
+ end
49
+
50
+ def orig_msg
51
+ @orig_msg ||= to_s.sub(msg_prefix, '')
52
+ end
53
+
54
+ def class_name
55
+ @class_name ||= self.class.to_s.sub('Checkup::Errors::', '')
56
+ end
57
+
58
+ def orig_err_class
59
+ return unless @orig_err
60
+
61
+ @orig_err_class ||= @orig_err.is_a?(Errors::Error) ?
62
+ @orig_err.send(:class_name) : @orig_err.class.to_s
63
+ end
64
+
65
+ def orig_err_msg
66
+ return unless @orig_err
67
+ return @orig_err_msg unless @orig_err_msg.nil?
68
+
69
+ msg = @orig_err.is_a?(Errors::Error) ?
70
+ @orig_err.send(:orig_msg) : @orig_err.to_s
71
+ @orig_err_msg = (msg == orig_err_class) ?
72
+ false : format_msg(msg)
73
+ end
74
+
75
+ def format_msg(msg)
76
+ msg.gsub(/^ */, ' ').strip
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,157 @@
1
+ # encoding: utf-8
2
+
3
+ module Checkup
4
+ module Logger
5
+ class << self
6
+
7
+ attr_accessor :quiet
8
+
9
+ ##
10
+ # Outputs a messages to the console and writes it to the backup.log
11
+ def message(string)
12
+ to_console loggify(string, :message, :green)
13
+ to_file loggify(string, :message)
14
+ end
15
+
16
+ ##
17
+ # Outputs an error to the console and writes it to the backup.log
18
+ # Called when an Exception has caused the backup process to abort.
19
+ def error(string)
20
+ @has_errors = true
21
+ to_console loggify(string, :error, :red), true
22
+ to_file loggify(string, :error)
23
+ end
24
+
25
+ ##
26
+ # Outputs a notice to the console and writes it to the backup.log
27
+ # Sets #has_warnings? true so :on_warning notifications will be sent
28
+ def warn(string)
29
+ @has_warnings = true
30
+ to_console loggify(string, :warning, :yellow), true
31
+ to_file loggify(string, :warning)
32
+ end
33
+
34
+ # Outputs the data as if it were a regular 'puts' command,
35
+ # but also logs it to the backup.log
36
+ def normal(string)
37
+ to_console loggify(string)
38
+ to_file loggify(string)
39
+ end
40
+
41
+ ##
42
+ # Silently logs data to the log file
43
+ def silent(string)
44
+ to_file loggify(string, :silent)
45
+ end
46
+
47
+ ##
48
+ # Returns an Array of all messages written to the log file for this session
49
+ def messages
50
+ @messages ||= []
51
+ end
52
+
53
+ def has_errors?
54
+ @has_errors ||= false
55
+ end
56
+
57
+ ##
58
+ # Returns true if any warnings have been issued
59
+ def has_warnings?
60
+ @has_warnings ||= false
61
+ end
62
+
63
+ def clear!
64
+ messages.clear
65
+ @has_warnings = false
66
+ end
67
+
68
+ def truncate!(max_bytes = 500_000)
69
+ log_file = File.join(Config.log_path, 'checkup.log')
70
+ return unless File.exist?(log_file)
71
+
72
+ if File.stat(log_file).size > max_bytes
73
+ FileUtils.mv(log_file, log_file + '~')
74
+ File.open(log_file + '~', 'r') do |io_in|
75
+ File.open(log_file, 'w') do |io_out|
76
+ io_in.seek(-max_bytes, IO::SEEK_END) && io_in.gets
77
+ while line = io_in.gets
78
+ io_out.puts line
79
+ end
80
+ end
81
+ end
82
+ FileUtils.rm_f(log_file + '~')
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ ##
89
+ # Returns the time in [YYYY/MM/DD HH:MM:SS] format
90
+ def time
91
+ Time.now.strftime("%Y/%m/%d %H:%M:%S")
92
+ end
93
+
94
+ ##
95
+ # Receives a String, or an Object that responds to #to_s (e.g. an
96
+ # Exception), from one of the messaging methods and converts it into an
97
+ # Array of Strings, split on newline separators. Each line is then
98
+ # formatted into a log format based on the given options, and the Array
99
+ # returned to be passed to to_console() and/or to_file().
100
+ def loggify(string, type = false, color = false)
101
+ lines = string.to_s.split("\n")
102
+ if type
103
+ type = send(color, type) if color
104
+ time_now = time
105
+ lines.map {|line| "[#{time_now}][#{type}] #{line}" }
106
+ else
107
+ lines
108
+ end
109
+ end
110
+
111
+ ##
112
+ # Receives an Array of Strings to be written to the console.
113
+ def to_console(lines, stderr = false)
114
+ return if quiet
115
+ lines.each {|line| stderr ? Kernel.warn(line) : puts(line) }
116
+ end
117
+
118
+ ##
119
+ # Receives an Array of Strings to be written to the log file.
120
+ def to_file(lines)
121
+ # File.open(File.join(Config.log_path, 'checkup.log'), 'a') do |file|
122
+ # lines.each {|line| file.puts line }
123
+ # end
124
+ # messages.push(*lines)
125
+ end
126
+
127
+ ##
128
+ # Invokes the #colorize method with the provided string
129
+ # and the color code "32" (for green)
130
+ def green(string)
131
+ colorize(string, 32)
132
+ end
133
+
134
+ ##
135
+ # Invokes the #colorize method with the provided string
136
+ # and the color code "33" (for yellow)
137
+ def yellow(string)
138
+ colorize(string, 33)
139
+ end
140
+
141
+ ##
142
+ # Invokes the #colorize method the with provided string
143
+ # and the color code "31" (for red)
144
+ def red(string)
145
+ colorize(string, 31)
146
+ end
147
+
148
+ ##
149
+ # Wraps the provided string in colorizing tags to provide
150
+ # easier to view output to the client
151
+ def colorize(string, code)
152
+ "\e[#{code}m#{string}\e[0m"
153
+ end
154
+
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,119 @@
1
+ module Checkup
2
+ class Model
3
+ class << self
4
+ def all
5
+ @all ||= []
6
+ end
7
+
8
+ def find(trigger)
9
+ trigger = trigger.to_s
10
+ all.each do |model|
11
+ return model if model.trigger == trigger
12
+ end
13
+ raise Errors::Model::MissingTriggerError,
14
+ "Could not find trigger '#{trigger}'."
15
+ end
16
+
17
+ def find_matching(trigger)
18
+ regex = /^#{ trigger.to_s.gsub('*', '(.*)') }$/
19
+ all.select {|model| regex =~ model.trigger }
20
+ end
21
+ end
22
+
23
+ attr_reader :trigger
24
+ attr_reader :description
25
+
26
+
27
+ def initialize(trigger, description, &block)
28
+ @trigger = trigger.to_s
29
+ @description = description.to_s
30
+ @started_at = Time.now
31
+
32
+ @services = []
33
+ @notifiers = []
34
+
35
+ instance_eval(&block) if block_given?
36
+ Model.all << self
37
+ end
38
+
39
+ def service(name, &block)
40
+ @services << get_class_from_scope(Service, name).new(self, &block)
41
+ end
42
+
43
+ def notify_with(name, &block)
44
+ @notifiers << get_class_from_scope(Notifier, name).new(self, &block)
45
+ end
46
+
47
+ def perform!
48
+ log!(:started)
49
+
50
+
51
+ @services.each do |service|
52
+ service_result = true
53
+ begin
54
+ Logger.message "Starting service #{service.identifier}"
55
+ service_result &&= service.perform!
56
+ rescue StandardError => e
57
+ service_result = false
58
+ self.notify! :error, service, "#{service.identifier}: #{e.message}"
59
+ Logger.error e.message
60
+ end
61
+
62
+ message = "Finished service #{service.identifier}"
63
+
64
+ if !service_result
65
+ message += " (with errors)"
66
+ Logger.error message
67
+ else
68
+ Logger.message message
69
+ end
70
+
71
+ end
72
+
73
+ log!(:finished)
74
+ end
75
+
76
+ def get_class_from_scope(scope, name)
77
+ klass = scope
78
+ name = name.to_s.sub(/^Checkup::Config::/, '')
79
+ name.split('::').each do |chunk|
80
+ klass = klass.const_get(chunk)
81
+ end
82
+ klass
83
+ end
84
+
85
+ def log!(action)
86
+ case action
87
+ when :started
88
+ Logger.message "Started model '(#{trigger})'!\n" +
89
+ "[ checkup #{ Version.current } : #{ RUBY_DESCRIPTION } ]"
90
+
91
+ when :finished
92
+ msg = "Finished '(#{ trigger })' " +
93
+ "Completed %s in #{ elapsed_time }"
94
+ if Logger.has_warnings?
95
+ Logger.warn msg % 'Successfully (with Warnings)'
96
+ elsif Logger.has_errors?
97
+ Logger.error msg % 'with errors'
98
+ else
99
+ Logger.message msg % 'Successfully'
100
+ end
101
+ end
102
+ end
103
+
104
+ def notify! (status, origin, message)
105
+ @notifiers.each do |notifier|
106
+ notifier.notify! status, origin, message
107
+ end
108
+ end
109
+
110
+ def elapsed_time
111
+ duration = Time.now.to_i - @started_at.to_i
112
+ hours = duration / 3600
113
+ remainder = duration - (hours * 3600)
114
+ minutes = remainder / 60
115
+ seconds = remainder - (minutes * 60)
116
+ '%02d:%02d:%02d' % [hours, minutes, seconds]
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,26 @@
1
+ module Checkup
2
+ module Notifier
3
+ class Base
4
+ include Checkup::Configuration::Helpers
5
+
6
+ attr_accessor :on_success
7
+ alias :notify_on_success? :on_success
8
+
9
+ attr_accessor :on_warning
10
+ alias :notify_on_warning? :on_warning
11
+
12
+ attr_accessor :on_failure
13
+ alias :notify_on_failure? :on_failure
14
+
15
+ def initialize(model)
16
+ @model = model
17
+ load_defaults!
18
+
19
+ yield self
20
+ end
21
+
22
+ def perform!
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,170 @@
1
+ # encoding: utf-8
2
+
3
+ Checkup::Dependency.load('mail')
4
+
5
+ module Checkup
6
+ module Notifier
7
+ class Mail < Base
8
+
9
+ ##
10
+ # Mail delivery method to be used by the Mail gem.
11
+ # Supported methods:
12
+ #
13
+ # `:smtp` [::Mail::SMTP] (default)
14
+ # : Settings used only by this method:
15
+ # : `address`, `port`, `domain`, `user_name`, `password`
16
+ # : `authentication`, `enable_starttls_auto`, `openssl_verify_mode`
17
+ #
18
+ # `:sendmail` [::Mail::Sendmail]
19
+ # : Settings used only by this method:
20
+ # : `sendmail`, `sendmail_args`
21
+ #
22
+ # `:exim` [::Mail::Exim]
23
+ # : Settings used only by this method:
24
+ # : `exim`, `exim_args`
25
+ #
26
+ # `:file` [::Mail::FileDelivery]
27
+ # : Settings used only by this method:
28
+ # : `mail_folder`
29
+ #
30
+ attr_accessor :delivery_method
31
+
32
+ ##
33
+ # Sender and Receiver email addresses
34
+ # Examples:
35
+ # sender - my.email.address@gmail.com
36
+ # receiver - your.email.address@gmail.com
37
+ attr_accessor :from, :to
38
+
39
+ ##
40
+ # The address to use
41
+ # Example: smtp.gmail.com
42
+ attr_accessor :address
43
+
44
+ ##
45
+ # The port to connect to
46
+ # Example: 587
47
+ attr_accessor :port
48
+
49
+ ##
50
+ # Your domain (if applicable)
51
+ # Example: mydomain.com
52
+ attr_accessor :domain
53
+
54
+ ##
55
+ # Username and Password (sender email's credentials)
56
+ # Examples:
57
+ # user_name - meskyanichi
58
+ # password - my_secret_password
59
+ attr_accessor :user_name, :password
60
+
61
+ ##
62
+ # Authentication type
63
+ # Example: plain
64
+ attr_accessor :authentication
65
+
66
+ ##
67
+ # Automatically set TLS
68
+ # Example: true
69
+ attr_accessor :enable_starttls_auto
70
+
71
+ ##
72
+ # OpenSSL Verify Mode
73
+ # Example: none - Only use this option for a self-signed and/or wildcard certificate
74
+ attr_accessor :openssl_verify_mode
75
+
76
+ ##
77
+ # When using the `:sendmail` `delivery_method` option,
78
+ # this may be used to specify the absolute path to `sendmail` (if needed)
79
+ # Example: '/usr/sbin/sendmail'
80
+ attr_accessor :sendmail
81
+
82
+ ##
83
+ # Optional arguments to pass to `sendmail`
84
+ # Note that this will override the defaults set by the Mail gem (currently: '-i -t')
85
+ # So, if set here, be sure to set all the arguments you require.
86
+ # Example: '-i -t -X/tmp/traffic.log'
87
+ attr_accessor :sendmail_args
88
+
89
+ ##
90
+ # When using the `:exim` `delivery_method` option,
91
+ # this may be used to specify the absolute path to `exim` (if needed)
92
+ # Example: '/usr/sbin/exim'
93
+ attr_accessor :exim
94
+
95
+ ##
96
+ # Optional arguments to pass to `exim`
97
+ # Note that this will override the defaults set by the Mail gem (currently: '-i -t')
98
+ # So, if set here, be sure to set all the arguments you require.
99
+ # Example: '-i -t -X/tmp/traffic.log'
100
+ attr_accessor :exim_args
101
+
102
+ ##
103
+ # Folder where mail will be kept when using the `:file` `delivery_method` option.
104
+ # Default location is '$HOME/Backup/emails'
105
+ # Example: '/tmp/test-mails'
106
+ attr_accessor :mail_folder
107
+
108
+ def initialize(model, &block)
109
+ super(model)
110
+
111
+ instance_eval(&block) if block_given?
112
+ end
113
+
114
+ def notify!(status, origin, message)
115
+ email = new_email
116
+ email.subject = "[Checkup::#{status}] #{@model.trigger}"
117
+ template = Checkup::Template.new ({ :origin => origin, :message => message })
118
+ email.body = template.result('notifier/mail/%s.erb' % status.to_s)
119
+
120
+ email.deliver!
121
+ end
122
+
123
+ private
124
+
125
+ ##
126
+ # Configures the Mail gem by setting the defaults.
127
+ # Creates and returns a new email, based on the @delivery_method used.
128
+ def new_email
129
+ method = %w{ smtp sendmail exim file test }.
130
+ index(@delivery_method.to_s) ? @delivery_method.to_s : 'smtp'
131
+
132
+ options =
133
+ case method
134
+ when 'smtp'
135
+ { :address => @address,
136
+ :port => @port,
137
+ :domain => @domain,
138
+ :user_name => @user_name,
139
+ :password => @password,
140
+ :authentication => @authentication,
141
+ :enable_starttls_auto => @enable_starttls_auto,
142
+ :openssl_verify_mode => @openssl_verify_mode }
143
+ when 'sendmail'
144
+ opts = {}
145
+ opts.merge!(:location => File.expand_path(@sendmail)) if @sendmail
146
+ opts.merge!(:arguments => @sendmail_args) if @sendmail_args
147
+ opts
148
+ when 'exim'
149
+ opts = {}
150
+ opts.merge!(:location => File.expand_path(@exim)) if @exim
151
+ opts.merge!(:arguments => @exim_args) if @exim_args
152
+ opts
153
+ when 'file'
154
+ @mail_folder ||= File.join(Config.root_path, 'emails')
155
+ { :location => File.expand_path(@mail_folder) }
156
+ when 'test' then {}
157
+ end
158
+
159
+ ::Mail.defaults do
160
+ delivery_method method.to_sym, options
161
+ end
162
+
163
+ email = ::Mail.new
164
+ email.to = @to
165
+ email.from = @from
166
+ email
167
+ end
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module Checkup
4
+ module Service
5
+ class Base
6
+ include Checkup::Configuration::Helpers
7
+
8
+ def initialize(model)
9
+ @model = model
10
+ load_defaults!
11
+
12
+ yield self
13
+ end
14
+
15
+ def perform!
16
+ end
17
+
18
+ def identifier
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ Checkup::Dependency.load('httparty')
4
+
5
+ module Checkup
6
+ module Service
7
+ class Http < Base
8
+ attr_accessor :url
9
+ attr_accessor :method
10
+ attr_accessor :expected_code
11
+ attr_accessor :expected_response
12
+
13
+ def perform!
14
+ super
15
+
16
+ response = ::HTTParty.send(self.method.to_sym, self.url)
17
+
18
+ Logger.message "Got #{response.code} from #{self.url}"
19
+
20
+ result = response.code == self.expected_code
21
+
22
+ if !result
23
+ raise Checkup::Errors::Service::Http::AssertError,
24
+ "Expected code #{self.expected_code}, got #{response.code}"
25
+ end
26
+
27
+ if self.expected_response.is_a? Regexp
28
+ result &&= !!self.expected_response.match(response.body)
29
+ if !result
30
+ raise Checkup::Errors::Service::Http::RegexpError,
31
+ "Expected match for #{self.expected_response.to_s}"
32
+ end
33
+ else
34
+ result &&= self.expected_response == response.body
35
+ raise Checkup::Errors::Service::Http::AssertError,
36
+ "Expected response #{self.expected_response}"
37
+ end
38
+
39
+ return result
40
+ end
41
+
42
+ def identifier
43
+ return "#{self.method} #{self.url}"
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,33 @@
1
+ require 'erb'
2
+
3
+ module Checkup
4
+ class Template
5
+
6
+ attr_accessor :binding
7
+
8
+ def initialize(object = nil)
9
+ if object.is_a?(Binding)
10
+ @binding = object
11
+ elsif object.is_a?(Hash)
12
+ @binding = Checkup::Binder.new(object).get_binding
13
+ else
14
+ @binding = nil
15
+ end
16
+ end
17
+
18
+ def render(file)
19
+ puts result(file)
20
+ end
21
+
22
+ def result(file)
23
+ ERB.new(file_contents(file), nil, '<>').result(binding)
24
+ end
25
+
26
+ private
27
+
28
+ def file_contents(file)
29
+ File.read(File.join(Checkup::TEMPLATE_PATH, file))
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ # encoding: utf-8
2
+
3
+ module Checkup
4
+ class Version
5
+
6
+ ##
7
+ # Change the MAJOR, MINOR and PATCH constants below
8
+ # to adjust the version of the Backup gem
9
+ #
10
+ # MAJOR:
11
+ # Defines the major version
12
+ # MINOR:
13
+ # Defines the minor version
14
+ # PATCH:
15
+ # Defines the patch version
16
+ MAJOR, MINOR, PATCH = 0, 0, 2
17
+
18
+ ##
19
+ # Returns the major version ( big release based off of multiple minor releases )
20
+ def self.major
21
+ MAJOR
22
+ end
23
+
24
+ ##
25
+ # Returns the minor version ( small release based off of multiple patches )
26
+ def self.minor
27
+ MINOR
28
+ end
29
+
30
+ ##
31
+ # Returns the patch version ( updates, features and (crucial) bug fixes )
32
+ def self.patch
33
+ PATCH
34
+ end
35
+
36
+ ##
37
+ # Returns the current version of the Backup gem ( qualified for the gemspec )
38
+ def self.current
39
+ "#{major}.#{minor}.#{patch}"
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ Service "<%= @origin.identifier %>" failed with
2
+
3
+ <%= @message %>
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: checkup
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Heiko Moeller
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: thor
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.14.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.14.6
30
+ - !ruby/object:Gem::Dependency
31
+ name: httparty
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description:
47
+ email: femaref@googlemail.com
48
+ executables:
49
+ - checkup
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - Gemfile.lock
56
+ - bin/checkup
57
+ - checkup.gemspec
58
+ - lib/checkup.rb
59
+ - lib/checkup/binder.rb
60
+ - lib/checkup/cli/utility.rb
61
+ - lib/checkup/config.rb
62
+ - lib/checkup/configuration/base.rb
63
+ - lib/checkup/configuration/helpers.rb
64
+ - lib/checkup/configuration/notifier/base.rb
65
+ - lib/checkup/configuration/notifier/mail.rb
66
+ - lib/checkup/configuration/service/base.rb
67
+ - lib/checkup/configuration/service/http.rb
68
+ - lib/checkup/dependency.rb
69
+ - lib/checkup/errors.rb
70
+ - lib/checkup/logger.rb
71
+ - lib/checkup/model.rb
72
+ - lib/checkup/notifier/base.rb
73
+ - lib/checkup/notifier/mail.rb
74
+ - lib/checkup/service/base.rb
75
+ - lib/checkup/service/http.rb
76
+ - lib/checkup/template.rb
77
+ - lib/checkup/version.rb
78
+ - templates/notifier/mail/error.erb
79
+ homepage: http://rubygems.org/gems/checkup
80
+ licenses: []
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ! '>='
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ none: false
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 1.8.23
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: ''
103
+ test_files: []