rubycron 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -0
- data/README.md +140 -0
- data/bin/rcjrunner.rb +11 -0
- data/lib/report.erb +25 -0
- data/lib/rubycron.rb +106 -0
- data/sample/test.rcj +28 -0
- metadata +64 -0
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
# RubyCron
|
2
|
+
|
3
|
+
Define your cronjobs in your favorite language, and get reporting for free!
|
4
|
+
|
5
|
+
By letting RubyCron deal with warnings, errors, and sending reports, you can focus on writing clean and effective cronjobs in Ruby.
|
6
|
+
|
7
|
+
## Author
|
8
|
+
|
9
|
+
* Bart Kamphorst
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
#> gem install rubycron
|
13
|
+
|
14
|
+
### Dependencies
|
15
|
+
|
16
|
+
This gem depends on [Mikel's wonderful mail gem](https://github.com/mikel/mail).
|
17
|
+
|
18
|
+
In order to send mail, it assumes you have a local smtp server running on port 25.
|
19
|
+
|
20
|
+
## Usage
|
21
|
+
|
22
|
+
### Configure the basics
|
23
|
+
|
24
|
+
Open a new file, require and include RubyCron, and then initialize a new RubyCronJob as follows:
|
25
|
+
|
26
|
+
rcj = RubyCronJob.new do |script|
|
27
|
+
script.author = 'John Doe'
|
28
|
+
script.name = 'test'
|
29
|
+
script.mailto = 'john@doe.com'
|
30
|
+
end
|
31
|
+
|
32
|
+
### Write your cronjob
|
33
|
+
|
34
|
+
Call RubyCronJob's execute method, and define your cronjob within the do-end block.
|
35
|
+
|
36
|
+
rcj.execute do
|
37
|
+
unless File.directory?('/tmp')
|
38
|
+
warning "Something awry is going on with /tmp."
|
39
|
+
end
|
40
|
+
begin
|
41
|
+
File.open('/tmp/rubycrontest', 'w') do |f|
|
42
|
+
f.write("Test completed successfully.")
|
43
|
+
end
|
44
|
+
rescue => e
|
45
|
+
error "Something went wrong trying to write to file: #{e.message}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
That's it! Now when you run this cronjob, you will receive a report by email.
|
50
|
+
|
51
|
+
## Set up Cron
|
52
|
+
|
53
|
+
To activate the cronjob, add it to your crontab like any other cronjob. There are, however, two ways to do this: run the RubyCronJob as a stand-alone script, or have the RubyCronJob be executed by rcjrunner.rb.
|
54
|
+
|
55
|
+
### Stand-alone
|
56
|
+
|
57
|
+
For the stand-alone option, add a crontab entry like so:
|
58
|
+
|
59
|
+
min hour mday month wday command
|
60
|
+
* * * * * test.rcj
|
61
|
+
|
62
|
+
For this to work properly, make sure your rubycronjob (test.rcj in this example) starts with a shebang and is executable.
|
63
|
+
|
64
|
+
### Using rcjrunner.rb
|
65
|
+
|
66
|
+
Simply feed your rubycronjob to rcjrunner.rb as a command-line argument in your crontab entry:
|
67
|
+
|
68
|
+
min hour mday month wday command
|
69
|
+
* * * * * rcjrunner.rb test.rcj
|
70
|
+
|
71
|
+
## Other configuration options
|
72
|
+
|
73
|
+
### I now get all these reports, but I really only care if the job fails!
|
74
|
+
|
75
|
+
Sorting through hundreds of cron mails per day that report successful runs may be gratifying at times, but most sane people only care to be notified when their cronjobs fail. Not to worry, just add to following line to the RubyCronJob's initialization.
|
76
|
+
|
77
|
+
script.mailon = :error
|
78
|
+
|
79
|
+
RubyCron will now only report when errors occurred during the run. Other options are :none, :warning and :all (default).
|
80
|
+
|
81
|
+
### All emails originate from root@localhost. Can I change that?
|
82
|
+
|
83
|
+
Of course. Use
|
84
|
+
|
85
|
+
script.mailfrom = 'root@doe.com'
|
86
|
+
|
87
|
+
to change the From:-header.
|
88
|
+
|
89
|
+
### I want my cronjob to stop running when there are errors.
|
90
|
+
|
91
|
+
No problem. You can configure this behavior with
|
92
|
+
|
93
|
+
script.exiton = :all
|
94
|
+
|
95
|
+
Valid values are :none, :warning, :error, :all.
|
96
|
+
|
97
|
+
### May I please see some output while I'm developing my cronjob?
|
98
|
+
|
99
|
+
Output to stdout and stderr can be very useful when debugging your cronjob. Just set the verbose flag to true:
|
100
|
+
|
101
|
+
script.verbose = true
|
102
|
+
|
103
|
+
### As a sysadmin, I like grepping through files. Can I have a log file please?
|
104
|
+
|
105
|
+
Yes. Set a file path in RubyCronJob's logfile variable, and all output will be redirected to file:
|
106
|
+
|
107
|
+
script.logfile = '/tmp/rcjlogfile'
|
108
|
+
|
109
|
+
Note that you will still receive email reports when you enable file logging.
|
110
|
+
|
111
|
+
## License
|
112
|
+
|
113
|
+
Copyright (c) 2011, Bart Kamphorst
|
114
|
+
|
115
|
+
(Modified BSD License)
|
116
|
+
|
117
|
+
All rights reserved.
|
118
|
+
|
119
|
+
Redistribution and use in source and binary forms, with or without
|
120
|
+
modification, are permitted provided that the following conditions are met:
|
121
|
+
|
122
|
+
* Redistributions of source code must retain the above copyright
|
123
|
+
notice, this list of conditions and the following disclaimer.
|
124
|
+
* Redistributions in binary form must reproduce the above copyright
|
125
|
+
notice, this list of conditions and the following disclaimer in the
|
126
|
+
documentation and/or other materials provided with the distribution.
|
127
|
+
* Neither the name of the organization nor the
|
128
|
+
names of its contributors may be used to endorse or promote products
|
129
|
+
derived from this software without specific prior written permission.
|
130
|
+
|
131
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
132
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
133
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
134
|
+
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
|
135
|
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
136
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
137
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
138
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
139
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
140
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/bin/rcjrunner.rb
ADDED
data/lib/report.erb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
This is your RubyCron mailer reporting on <%= self.name %>.
|
2
|
+
|
3
|
+
There were <%= @warnings.size %> warnings and <%= @errors.size %> errors during the last run, which started at
|
4
|
+
<%= @starttime %> and ended at <%= @endtime %>.
|
5
|
+
<% unless @warnings.empty? %>
|
6
|
+
<%= "These were the warnings:" %>
|
7
|
+
<% @warnings.each_with_index do |warning, index| %>
|
8
|
+
<%= index + 1 %>: <%= warning %>
|
9
|
+
<%= "============================================================" %>
|
10
|
+
<% end %> <% end %>
|
11
|
+
<% unless @errors.empty? %>
|
12
|
+
<%= "These were the errors:" %>
|
13
|
+
<% @errors.each_with_index do |error, index| %>
|
14
|
+
<%= index + 1 %>: <%= error %>
|
15
|
+
<%= "============================================================" %>
|
16
|
+
<% end %> <% end %>
|
17
|
+
<% if @warnings.empty? && @errors.empty? %>
|
18
|
+
<%= "There were no problems to report. Have a nice day!" %>
|
19
|
+
<% else %>
|
20
|
+
<%= "Please look into these matters asap." %>
|
21
|
+
<% end %>
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
## Powered by RubyCron.
|
data/lib/rubycron.rb
ADDED
@@ -0,0 +1,106 @@
|
|
1
|
+
# Copyright (c) Bart Kamphorst <rubycron@kamphorst.com>, 2011
|
2
|
+
# Licensed under the modified BSD License. All rights reserved.
|
3
|
+
|
4
|
+
module RubyCron
|
5
|
+
|
6
|
+
class RubyCronJob
|
7
|
+
|
8
|
+
require 'net/smtp'
|
9
|
+
require 'rubygems'
|
10
|
+
require 'mail'
|
11
|
+
require 'erb'
|
12
|
+
|
13
|
+
attr_accessor :name, :author, :mailto, :mailfrom, :mailsubject, :mailon, :exiton, :logfile, :verbose
|
14
|
+
|
15
|
+
def initialize(&block)
|
16
|
+
@warnings, @errors = [], []
|
17
|
+
|
18
|
+
instance_eval(&block)
|
19
|
+
terminate("Cannot connect to local smtp server.") unless smtp_connection?
|
20
|
+
terminate("This job has no name.") unless self.name
|
21
|
+
terminate("This job has no author.") unless self.author
|
22
|
+
terminate("No To: header was set. ") unless self.mailto
|
23
|
+
|
24
|
+
self.mailfrom ||= 'root@localhost'
|
25
|
+
self.verbose ||= false
|
26
|
+
self.mailon = :all unless self.mailon && [:none, :warning, :error, :all].include?(self.mailon)
|
27
|
+
self.exiton = :all unless self.exiton && [:none, :warning, :error, :all].include?(self.exiton)
|
28
|
+
|
29
|
+
if self.logfile
|
30
|
+
$stdout.reopen(self.logfile, "a")
|
31
|
+
$stdout.sync = true
|
32
|
+
$stderr.reopen($stdout)
|
33
|
+
end
|
34
|
+
rescue => e
|
35
|
+
$stdout = STDOUT
|
36
|
+
terminate(e.message)
|
37
|
+
end
|
38
|
+
|
39
|
+
def terminate(message)
|
40
|
+
$stderr.puts "## Cannot complete job. Reason: #{message}"
|
41
|
+
exit 1
|
42
|
+
end
|
43
|
+
|
44
|
+
# Execute a given block of code (the cronjob), rescue encountered errors,
|
45
|
+
# and send a report about it if necessary.
|
46
|
+
def execute(&block)
|
47
|
+
@starttime = Time.now
|
48
|
+
puts "\nStarting run of #{self.name} at #{@starttime}.\n----" if self.verbose || self.logfile
|
49
|
+
instance_eval(&block)
|
50
|
+
rescue Exception => e
|
51
|
+
@errors << e.message
|
52
|
+
terminate(e.message) if exiton == (:error || :all)
|
53
|
+
ensure
|
54
|
+
@endtime = Time.now
|
55
|
+
if self.verbose || self.logfile
|
56
|
+
puts "Run ended at #{@endtime}.\n----"
|
57
|
+
puts "Number of warnings: #{@warnings.size}"
|
58
|
+
puts "Number of errors : #{@errors.size}"
|
59
|
+
end
|
60
|
+
unless self.mailon == :none || (@warnings.empty? && @errors.empty? && self.mailon != :all)
|
61
|
+
report
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def warning(message)
|
66
|
+
$stderr.puts message if self.verbose || self.logfile
|
67
|
+
raise "Configured to exit on warning." if exiton == (:warning || :all)
|
68
|
+
@warnings << message
|
69
|
+
end
|
70
|
+
|
71
|
+
def error(message)
|
72
|
+
$stderr.puts message if self.verbose || self.logfile
|
73
|
+
raise "Configured to exit on error." if exiton == (:error || :all)
|
74
|
+
@errors << message
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
def smtp_connection?
|
79
|
+
return true if Net::SMTP.start('localhost', 25)
|
80
|
+
rescue
|
81
|
+
return false
|
82
|
+
end
|
83
|
+
|
84
|
+
# Report on the status of the cronjob through the use of
|
85
|
+
# an erb template file, and mikel's excellent mail gem.
|
86
|
+
private
|
87
|
+
def report
|
88
|
+
self.mailsubject = "Cron report for #{name}: #{@warnings.size} warnings & #{@errors.size} errors" unless self.mailsubject
|
89
|
+
mailfrom = self.mailfrom
|
90
|
+
mailto = self.mailto
|
91
|
+
mailsubject = self.mailsubject
|
92
|
+
mailbody = ERB.new(File.read(File.join(File.dirname(__FILE__), '/report.erb'))).result(binding)
|
93
|
+
|
94
|
+
mail = Mail.new do
|
95
|
+
from mailfrom
|
96
|
+
to mailto
|
97
|
+
subject mailsubject
|
98
|
+
body mailbody
|
99
|
+
end
|
100
|
+
|
101
|
+
mail.deliver!
|
102
|
+
rescue => e
|
103
|
+
terminate(e.message)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/sample/test.rcj
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'RubyCron'
|
5
|
+
include RubyCron
|
6
|
+
|
7
|
+
rcj = RubyCronJob.new do |script|
|
8
|
+
script.author = 'John Doe'
|
9
|
+
script.name = 'test'
|
10
|
+
script.mailto = 'john@doe.com'
|
11
|
+
script.mailfrom = 'root@doe.com'
|
12
|
+
script.mailon = :all
|
13
|
+
script.exiton = :none
|
14
|
+
script.verbose = false
|
15
|
+
end
|
16
|
+
|
17
|
+
rcj.execute do
|
18
|
+
unless File.directory?('/tmp')
|
19
|
+
warning "Something awry is going on with /tmp."
|
20
|
+
end
|
21
|
+
begin
|
22
|
+
File.open('/tmp/rubycrontest', 'w') do |f|
|
23
|
+
f.write("Test completed successfully.")
|
24
|
+
end
|
25
|
+
rescue => e
|
26
|
+
error "Something went wrong trying to write to file: #{e.message}"
|
27
|
+
end
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rubycron
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bart Kamphorst
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-10-19 00:00:00.000000000 +02:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: mail
|
17
|
+
requirement: &2160625260 !ruby/object:Gem::Requirement
|
18
|
+
none: false
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: *2160625260
|
26
|
+
description: Write clean cronjobs in Ruby, and get reporting for free!
|
27
|
+
email: rubycron@kamphorst.com
|
28
|
+
executables: []
|
29
|
+
extensions: []
|
30
|
+
extra_rdoc_files:
|
31
|
+
- README.md
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- Gemfile
|
35
|
+
- lib/rubycron.rb
|
36
|
+
- lib/report.erb
|
37
|
+
- sample/test.rcj
|
38
|
+
- bin/rcjrunner.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: https://github.com/bartkamphorst/rubycron
|
41
|
+
licenses: []
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.6.2
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Simplifies your Ruby cronjobs by automating the reporting process.
|
64
|
+
test_files: []
|