minicron 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +674 -0
- data/README.md +187 -0
- data/Rakefile +17 -0
- data/bin/minicron +26 -0
- data/lib/minicron.rb +179 -0
- data/lib/minicron/alert.rb +115 -0
- data/lib/minicron/alert/email.rb +50 -0
- data/lib/minicron/alert/pagerduty.rb +39 -0
- data/lib/minicron/alert/sms.rb +47 -0
- data/lib/minicron/cli.rb +367 -0
- data/lib/minicron/constants.rb +7 -0
- data/lib/minicron/cron.rb +192 -0
- data/lib/minicron/hub/app.rb +132 -0
- data/lib/minicron/hub/assets/app/application.js +151 -0
- data/lib/minicron/hub/assets/app/components/schedules.js +280 -0
- data/lib/minicron/hub/assets/app/controllers/executions.js +35 -0
- data/lib/minicron/hub/assets/app/controllers/hosts.js +129 -0
- data/lib/minicron/hub/assets/app/controllers/jobs.js +109 -0
- data/lib/minicron/hub/assets/app/controllers/schedules.js +80 -0
- data/lib/minicron/hub/assets/app/helpers.js +22 -0
- data/lib/minicron/hub/assets/app/models/execution.js +13 -0
- data/lib/minicron/hub/assets/app/models/host.js +15 -0
- data/lib/minicron/hub/assets/app/models/job.js +15 -0
- data/lib/minicron/hub/assets/app/models/job_execution_output.js +11 -0
- data/lib/minicron/hub/assets/app/models/schedule.js +32 -0
- data/lib/minicron/hub/assets/app/router.js +31 -0
- data/lib/minicron/hub/assets/app/routes/executions.js +36 -0
- data/lib/minicron/hub/assets/app/routes/hosts.js +42 -0
- data/lib/minicron/hub/assets/app/routes/index.js +9 -0
- data/lib/minicron/hub/assets/app/routes/jobs.js +52 -0
- data/lib/minicron/hub/assets/app/routes/schedules.js +37 -0
- data/lib/minicron/hub/assets/css/bootswatch.min.css +9 -0
- data/lib/minicron/hub/assets/css/main.scss +323 -0
- data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.svg +229 -0
- data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/minicron/hub/assets/fonts/lato-bold-700.woff +0 -0
- data/lib/minicron/hub/assets/fonts/lato-italic-400.woff +0 -0
- data/lib/minicron/hub/assets/fonts/lato-regular-400.woff +0 -0
- data/lib/minicron/hub/assets/js/ansi_up-1.1.1.min.js +6 -0
- data/lib/minicron/hub/assets/js/auth/ember-auth-9.0.7.min.js +2 -0
- data/lib/minicron/hub/assets/js/auth/ember-auth-request-jquery-1.0.3.min.js +1 -0
- data/lib/minicron/hub/assets/js/bootstrap-3.1.1.min.js +6 -0
- data/lib/minicron/hub/assets/js/ember-1.4.1.min.js +18 -0
- data/lib/minicron/hub/assets/js/ember-data-1.0.0-beta.7.f87cba88.min.js +10 -0
- data/lib/minicron/hub/assets/js/faye-browser-1.0.1.min.js +2 -0
- data/lib/minicron/hub/assets/js/handlebars-1.3.0.min.js +29 -0
- data/lib/minicron/hub/assets/js/jquery-2.1.0.min.js +4 -0
- data/lib/minicron/hub/assets/js/moment-2.5.1.min.js +7 -0
- data/lib/minicron/hub/controllers/api/executions.rb +34 -0
- data/lib/minicron/hub/controllers/api/hosts.rb +150 -0
- data/lib/minicron/hub/controllers/api/job_execution_outputs.rb +30 -0
- data/lib/minicron/hub/controllers/api/jobs.rb +118 -0
- data/lib/minicron/hub/controllers/api/schedule.rb +184 -0
- data/lib/minicron/hub/controllers/index.rb +5 -0
- data/lib/minicron/hub/db/schema.rb +98 -0
- data/lib/minicron/hub/db/schema.sql +158 -0
- data/lib/minicron/hub/models/alert.rb +7 -0
- data/lib/minicron/hub/models/execution.rb +8 -0
- data/lib/minicron/hub/models/host.rb +7 -0
- data/lib/minicron/hub/models/job.rb +18 -0
- data/lib/minicron/hub/models/job_execution_output.rb +7 -0
- data/lib/minicron/hub/models/schedule.rb +25 -0
- data/lib/minicron/hub/serializers/execution.rb +75 -0
- data/lib/minicron/hub/serializers/host.rb +57 -0
- data/lib/minicron/hub/serializers/job.rb +104 -0
- data/lib/minicron/hub/serializers/job_execution_output.rb +48 -0
- data/lib/minicron/hub/serializers/schedule.rb +68 -0
- data/lib/minicron/hub/views/handlebars/application.erb +51 -0
- data/lib/minicron/hub/views/handlebars/errors.erb +29 -0
- data/lib/minicron/hub/views/handlebars/executions.erb +79 -0
- data/lib/minicron/hub/views/handlebars/hosts.erb +205 -0
- data/lib/minicron/hub/views/handlebars/jobs.erb +203 -0
- data/lib/minicron/hub/views/handlebars/loading.erb +3 -0
- data/lib/minicron/hub/views/handlebars/schedules.erb +354 -0
- data/lib/minicron/hub/views/index.erb +7 -0
- data/lib/minicron/hub/views/layouts/app.erb +15 -0
- data/lib/minicron/monitor.rb +116 -0
- data/lib/minicron/transport.rb +15 -0
- data/lib/minicron/transport/client.rb +80 -0
- data/lib/minicron/transport/faye/client.rb +103 -0
- data/lib/minicron/transport/faye/extensions/job_handler.rb +184 -0
- data/lib/minicron/transport/faye/server.rb +58 -0
- data/lib/minicron/transport/server.rb +62 -0
- data/lib/minicron/transport/ssh.rb +51 -0
- data/spec/invalid_config.toml +2 -0
- data/spec/minicron/cli_spec.rb +154 -0
- data/spec/minicron/transport/client_spec.rb +8 -0
- data/spec/minicron/transport/faye/client_spec.rb +53 -0
- data/spec/minicron/transport/server_spec.rb +70 -0
- data/spec/minicron/transport_spec.rb +13 -0
- data/spec/minicron_spec.rb +133 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/valid_config.toml +48 -0
- metadata +577 -0
data/README.md
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
minicron
|
2
|
+
=======
|
3
|
+
|
4
|
+
[![Build Status](http://img.shields.io/travis/jamesrwhite/minicron.svg)](http://travis-ci.org/jamesrwhite/minicron) [![Coverage Status](http://img.shields.io/coveralls/jamesrwhite/minicron.svg)](https://coveralls.io/r/jamesrwhite/minicron?branch=master) [![Code Climate](http://img.shields.io/codeclimate/github/jamesrwhite/minicron.svg)](https://codeclimate.com/github/jamesrwhite/minicron) [![Dependency Status](http://img.shields.io/gemnasium/jamesrwhite/minicron.svg)](https://gemnasium.com/jamesrwhite/minicron) [![Inline docs](http://inch-pages.github.io/github/jamesrwhite/minicron.png)](http://inch-pages.github.io/github/jamesrwhite/minicron)
|
5
|
+
|
6
|
+
minicron aims to complement ````cron```` by making it easier to manage and monitor cron jobs, it can largely be thought of as two components that interact together, the CLI and the Hub. The CLI is what is installed on your server(s) and executes your cron command and reports the status back to the Hub. The Hub is the central point where data from one or many instances of the CLI are is recieved and stored in a database. The Hub also provides a web interface to the data and makes it easy to manage your cron jobs.
|
7
|
+
|
8
|
+
- [Background](https://github.com/jamesrwhite/minicron/blob/master/README.md#background)
|
9
|
+
- [Goals](https://github.com/jamesrwhite/minicron/blob/master/README.md#features)
|
10
|
+
- [Features](https://github.com/jamesrwhite/minicron/blob/master/README.md#goals)
|
11
|
+
- [Installation](https://github.com/jamesrwhite/minicron/blob/master/README.md#installation)
|
12
|
+
- [Requirements](https://github.com/jamesrwhite/minicron/blob/master/README.md#requirements)
|
13
|
+
- [Usage](https://github.com/jamesrwhite/minicron/blob/master/README.md#usage)
|
14
|
+
- [Documentation](https://github.com/jamesrwhite/minicron/blob/master/README.md#documentation)
|
15
|
+
- [Versioning](https://github.com/jamesrwhite/minicron/blob/master/README.md#versioning)
|
16
|
+
- [Contributing](https://github.com/jamesrwhite/minicron/blob/master/README.md#contributing)
|
17
|
+
- [Support](https://github.com/jamesrwhite/minicron/blob/master/README.md#support)
|
18
|
+
- [License](https://github.com/jamesrwhite/minicron/blob/master/README.md#license)
|
19
|
+
|
20
|
+
Background
|
21
|
+
-----------
|
22
|
+
|
23
|
+
I'm developing minicron as part of my dissertation at university which is due in May but I plan to continue development after that. My inspiration for developing minicron comes from time spent working at [Miniclip](http://www.miniclip.com) where the management and monitoring of cron jobs at times proved to be tricky!
|
24
|
+
|
25
|
+
Goals
|
26
|
+
------
|
27
|
+
|
28
|
+
Some rough goals that minicron is trying to achieve.
|
29
|
+
|
30
|
+
- Remove the need to understand cron syntax
|
31
|
+
- Increase visiblity into cron failures and missed executions
|
32
|
+
- Have minimal external dependencies
|
33
|
+
- Fault tolerance
|
34
|
+
|
35
|
+
Features
|
36
|
+
---------
|
37
|
+
|
38
|
+
- Web UI
|
39
|
+
- CRUD for cron jobs using ssh
|
40
|
+
- GUI for cron schedule create/read/update
|
41
|
+
- View realtime output/status as jobs run
|
42
|
+
- Historical data for all job executions
|
43
|
+
- Alerts when jobs executions are missed or fail via:
|
44
|
+
- Email
|
45
|
+
- SMS ([using Twilio](https://www.twilio.com))
|
46
|
+
- [PagerDuty](www.pagerduty.com)
|
47
|
+
|
48
|
+
Lots more is planned for the feature, see issues tagged [feature](https://github.com/jamesrwhite/minicron/issues?labels=feature&milestone=&page=1&state=open).
|
49
|
+
|
50
|
+
Installation
|
51
|
+
-------------
|
52
|
+
|
53
|
+
minicron is currently under heavy development and as such I would not recommend that you use this in production yet but I encourage you to give it a try in a non critical environment and help me improve it.
|
54
|
+
|
55
|
+
#### Development Install
|
56
|
+
|
57
|
+
If you wish to test the latest from GitHub version you can clone this repo ````bundle install```` and ````rake install````.
|
58
|
+
|
59
|
+
#### Released Install
|
60
|
+
|
61
|
+
To install the latest release (currently 0.1) you can ````gem install minicron````.
|
62
|
+
|
63
|
+
#### Database Setup
|
64
|
+
|
65
|
+
Set your database configuration options in ````/etc/minicron.toml```` and you can then ````minicron db setup````
|
66
|
+
|
67
|
+
> **WARNING** this will drop any existing tables in the configured database and create new ones
|
68
|
+
|
69
|
+
or set it up manually using the [schema dump provided](https://github.com/jamesrwhite/minicron/blob/master/lib/minicron/hub/db/schema.sql).
|
70
|
+
|
71
|
+
Requirements
|
72
|
+
-------------
|
73
|
+
|
74
|
+
#### Ruby
|
75
|
+
- MRI
|
76
|
+
- 1.9.3 and above (tested on 1.9.3, 2.0.0, 2.1.0)
|
77
|
+
- <del>Rubinius</del>
|
78
|
+
- <del>Travis builds are run on the latest release</del> [*awaiting bug fix*](https://github.com/rubinius/rubinius/issues/2944)
|
79
|
+
- JRuby
|
80
|
+
- As yet untested
|
81
|
+
|
82
|
+
|
83
|
+
#### Database
|
84
|
+
- MySQL
|
85
|
+
- Support for PostgreSQL and SQlite is planned in the future
|
86
|
+
|
87
|
+
#### Browser
|
88
|
+
- I have been testing the web interface in the latest versions of Chrome, Firefox and Safari. I'm currently unsure of how it functions in the various of Internet Explorer but in theory it should support IE9+
|
89
|
+
|
90
|
+
#### OS
|
91
|
+
- Should run on any linux/bsd based OS that the above ruby versions run on.
|
92
|
+
- No windows support.
|
93
|
+
|
94
|
+
Usage
|
95
|
+
-----
|
96
|
+
|
97
|
+
minicron is packaged as a gem and can be interacted with from the command line once you have installed it. The current commands are as follows:
|
98
|
+
|
99
|
+
#### Run a command
|
100
|
+
|
101
|
+
````
|
102
|
+
minicron run 'command --options'
|
103
|
+
````
|
104
|
+
|
105
|
+
You can alter the way in which the command alters it's output by passing the --mode option to the run argument with the value of either 'line' or 'char'. Most of the time you'll want to use the default value of line but for some commands e.g a script that outputs a progress bar minicron printing the output each character at a time can be useful.
|
106
|
+
|
107
|
+
````
|
108
|
+
minicron run 'mysqldump db > backup.sql'
|
109
|
+
````
|
110
|
+
|
111
|
+
The global ````--verbose```` option can also be passed to the ````run```` argument like so ````minicron run --verbose ls````, for further information see ````minicron help run````.
|
112
|
+
|
113
|
+
#### Get help
|
114
|
+
|
115
|
+
Running ````minicron```` with no arguments is an alias to running ````minicron help````, ````minicron -h```` or ````minicron --help````. You can also use the help argument to get information on any command as shown above in the run a command section or alternatively you can pass the ````-h```` or ````--help```` options like so ````minicron run -h````.
|
116
|
+
|
117
|
+
#### Server
|
118
|
+
|
119
|
+
To launch the server (aka the Hub) run ````minicron server```` - by default it will bind to port 9292 on the host 127.0.0.1 but this can be configured by the command line arguments ````--host```` ````--port```` and ````--path```` or in the config file.
|
120
|
+
|
121
|
+
See [sample.nginx.conf](https://github.com/jamesrwhite/minicron/blob/master/sample.nginx.conf) for an example of how to run minicron behind a reverse proxy. It should be possible to run minicron behind any web server/reverse proxy as long as it supports WebSockets.
|
122
|
+
|
123
|
+
#### Version
|
124
|
+
|
125
|
+
Like many command line programs minicron will show it's version number when the global options ````-v```` or ````--version```` are passed to the CLI.
|
126
|
+
|
127
|
+
#### Configuration
|
128
|
+
|
129
|
+
Some configuration options can be passed in manually but the recommend way to configure minicron is through the use of a config file. You can specify the path to the file using the ````--config```` global option. The file is expected to be in the [toml](https://github.com/mojombo/toml "toml") format. The default options are specified in the [default.config.toml](https://github.com/jamesrwhite/minicron/blob/master/default.config.toml "default.config.toml") file and minicron will parse a config located in ````/etc/minicron.toml```` if it exists. Options specified via the command line will take precedence over those taken from a config file.
|
130
|
+
|
131
|
+
Documentation
|
132
|
+
-------------
|
133
|
+
|
134
|
+
minicron uses [Yard](http://yardoc.org/ "Yard") for it's code documentation, you can either generate it and view it locally using the following commands:
|
135
|
+
|
136
|
+
````
|
137
|
+
yard doc
|
138
|
+
yard server
|
139
|
+
````
|
140
|
+
|
141
|
+
or view the most up to date version online at [RubyDoc.info](http://rdoc.info/github/jamesrwhite/minicron/master/frames "RubyDoc.info").
|
142
|
+
|
143
|
+
Versioning
|
144
|
+
-----------
|
145
|
+
|
146
|
+
Where possible all releases will follow the [semantic versioning](http://semver.org/) guidelines.
|
147
|
+
|
148
|
+
Releases will be numbered with the following format:
|
149
|
+
|
150
|
+
`<major>.<minor>.<patch>`
|
151
|
+
|
152
|
+
Based on the following guidelines:
|
153
|
+
|
154
|
+
* A new *major* release indicates a large change where backwards compatibility is broken.
|
155
|
+
* A new *minor* release indicates a normal change that maintains backwards compatibility.
|
156
|
+
* A new *patch* release indicates a bugfix or small change which does not affect compatibility.
|
157
|
+
|
158
|
+
Contributing
|
159
|
+
------------
|
160
|
+
|
161
|
+
Feedback and pull requests are welcome, please see [CONTRIBUTING.md](https://github.com/jamesrwhite/minicron/blob/master/CONTRIBUTING.md "CONTRIBUTING.md") for more info.
|
162
|
+
|
163
|
+
Areas that I would love some help with:
|
164
|
+
|
165
|
+
- Any of the unassigned [issues here](https://github.com/jamesrwhite/minicron/issues?state=open).
|
166
|
+
- General testing of the system, let me know what you think and create issues for any bugs you find!
|
167
|
+
- Tests!!
|
168
|
+
- Validation and error handling improvements
|
169
|
+
- Documentation improvements. Find something confusing or unexpected, let me know and I'll add or improve documentation for it!
|
170
|
+
- Look for '[TODO:](https://github.com/jamesrwhite/minicron/search?q=TODO%3A)' notices littered around the code, I'm trying to convert them all to issues but there are a lot..
|
171
|
+
- Code refactoring, I had to have 0.1 ready by a certain deadline so some parts are far from perfect
|
172
|
+
- UI improvements
|
173
|
+
|
174
|
+
Support
|
175
|
+
--------
|
176
|
+
|
177
|
+
Where possible I will try and provide support for minicron, you can get in touch with me via:
|
178
|
+
|
179
|
+
- Twitter [@jamesrwhite](https://twitter.com/jamesrwhite)
|
180
|
+
- Email [dev.jameswhite+minicron@gmail.com](mailto:dev.jameswhite+minicron@gmail.com)
|
181
|
+
|
182
|
+
Or feel free to open an issue and I'll do my best to help.
|
183
|
+
|
184
|
+
License
|
185
|
+
--------
|
186
|
+
|
187
|
+
minicron is licensed under the GPL v3, [see here for the full license](https://github.com/jamesrwhite/minicron/blob/master/LICENSE "see here")
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rspec/core/rake_task'
|
3
|
+
require 'sinatra/activerecord/rake'
|
4
|
+
require 'minicron'
|
5
|
+
require 'minicron/hub/app'
|
6
|
+
|
7
|
+
# rspec tests
|
8
|
+
desc 'Run specs'
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
10
|
+
t.verbose = true
|
11
|
+
t.rspec_opts = '--color --order random'
|
12
|
+
end
|
13
|
+
|
14
|
+
# Show a list of tasks by default
|
15
|
+
task :default do
|
16
|
+
puts `rake --tasks`
|
17
|
+
end
|
data/bin/minicron
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'minicron'
|
3
|
+
|
4
|
+
begin
|
5
|
+
# Capture any output from STDERR so we can handle it how we want to
|
6
|
+
Minicron.capture_output(:type => :stderr) do
|
7
|
+
# Run the CLI and pass it all arguments passed to us
|
8
|
+
Minicron::CLI.new.run(ARGV) do |output|
|
9
|
+
print output
|
10
|
+
STDOUT.flush
|
11
|
+
end
|
12
|
+
end
|
13
|
+
# Handle ctrl-c and other nasty interruptions
|
14
|
+
rescue Interrupt, SignalException
|
15
|
+
puts "\nExiting.."
|
16
|
+
# Accept failure
|
17
|
+
rescue Exception => e
|
18
|
+
$stderr.write("#{e.message}\n")
|
19
|
+
|
20
|
+
if Minicron.config['cli']['trace']
|
21
|
+
puts
|
22
|
+
fail e
|
23
|
+
end
|
24
|
+
|
25
|
+
exit(1)
|
26
|
+
end
|
data/lib/minicron.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'toml'
|
3
|
+
require 'sshkey'
|
4
|
+
require 'minicron/cli'
|
5
|
+
require 'minicron/constants'
|
6
|
+
|
7
|
+
# @author James White <dev.jameswhite+minicron@gmail.com>
|
8
|
+
module Minicron
|
9
|
+
# Default configuration, this can be overriden
|
10
|
+
@config = {
|
11
|
+
'global' => {
|
12
|
+
'verbose' => false,
|
13
|
+
'trace' => false
|
14
|
+
},
|
15
|
+
'client' => {
|
16
|
+
'scheme' => 'http',
|
17
|
+
'host' => '127.0.0.1',
|
18
|
+
'port' => 9292,
|
19
|
+
'path' => '/',
|
20
|
+
'connect_timeout' => 5,
|
21
|
+
'inactivity_timeout' => 5
|
22
|
+
},
|
23
|
+
'server' => {
|
24
|
+
'host' => '127.0.0.1',
|
25
|
+
'port' => 9292,
|
26
|
+
'path' => '/'
|
27
|
+
},
|
28
|
+
'database' => {
|
29
|
+
'type' => 'mysql',
|
30
|
+
'host' => '127.0.0.1',
|
31
|
+
'database' => 'minicron',
|
32
|
+
'username' => 'minicron',
|
33
|
+
'password' => 'password'
|
34
|
+
},
|
35
|
+
'cli' => {
|
36
|
+
'mode' => 'line',
|
37
|
+
'dry_run' => false
|
38
|
+
},
|
39
|
+
'alerts' => {
|
40
|
+
'email' => {
|
41
|
+
'enabled' => false,
|
42
|
+
'smtp' => {
|
43
|
+
'address' => 'localhost',
|
44
|
+
'port' => 25
|
45
|
+
}
|
46
|
+
},
|
47
|
+
'sms' => {
|
48
|
+
'enabled' => false
|
49
|
+
},
|
50
|
+
'pagerduty' => {
|
51
|
+
'enabled' => false
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
class << self
|
57
|
+
attr_accessor :config
|
58
|
+
end
|
59
|
+
|
60
|
+
# Parse the given config file and update the config hash
|
61
|
+
#
|
62
|
+
# @param file_path [String]
|
63
|
+
def self.parse_file_config(file_path)
|
64
|
+
file_path ||= Minicron::DEFAULT_CONFIG_FILE
|
65
|
+
|
66
|
+
begin
|
67
|
+
@config = TOML.load_file(file_path)
|
68
|
+
rescue Errno::ENOENT
|
69
|
+
# Fail if the file doesn't exist unless it's the default config file
|
70
|
+
if file_path != DEFAULT_CONFIG_FILE
|
71
|
+
fail Exception, "Unable to the load the file '#{file_path}', are you sure it exists?"
|
72
|
+
end
|
73
|
+
rescue Errno::EACCES
|
74
|
+
fail Exception, "Unable to the readthe file '#{file_path}', check it has the right permissions."
|
75
|
+
rescue TOML::ParseError
|
76
|
+
fail Exception, "An error occured parsing the config file '#{file_path}', please check it uses valid TOML syntax."
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Parses the config options from the cli
|
81
|
+
# @option options [Hash] global global options
|
82
|
+
# @option options [Hash] server server options
|
83
|
+
# @option options [Hash] cli cli options
|
84
|
+
def self.parse_cli_config(options = {})
|
85
|
+
options.each do |type, _|
|
86
|
+
options[type].each do |key, value|
|
87
|
+
if value
|
88
|
+
@config[type][key] = value
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Helper function to capture STDOUT and/or STDERR
|
95
|
+
# adapted from http://stackoverflow.com/a/11349621/483271
|
96
|
+
#
|
97
|
+
# @option options [Symbol] type (:both) what to capture: :stdout, :stderr or :both
|
98
|
+
# @return [StringIO] if the type was set to :stdout or :stderr
|
99
|
+
# @return [Hash] containg both the StringIO instances if the type was set to :both
|
100
|
+
def self.capture_output(options = {}, &block)
|
101
|
+
# Default options
|
102
|
+
options[:type] ||= :both
|
103
|
+
|
104
|
+
# Make copies of the origin STDOUT/STDERR
|
105
|
+
original_stdout = $stdout
|
106
|
+
original_stderr = $stderr
|
107
|
+
|
108
|
+
# Which are we handling?
|
109
|
+
case options[:type]
|
110
|
+
when :stdout
|
111
|
+
$stdout = stdout = StringIO.new
|
112
|
+
when :stderr
|
113
|
+
$stderr = stderr = StringIO.new
|
114
|
+
when :both
|
115
|
+
$stderr = $stdout = stdout = stderr = StringIO.new
|
116
|
+
else
|
117
|
+
fail ArgumentError, 'The type must be one of [stdout, stderr, both]'
|
118
|
+
end
|
119
|
+
|
120
|
+
# Yield to the code block to do whatever it has to do
|
121
|
+
begin
|
122
|
+
yield
|
123
|
+
# Whatever happens make sure we reset STDOUT/STDERR
|
124
|
+
ensure
|
125
|
+
$stdout = original_stdout
|
126
|
+
$stderr = original_stderr
|
127
|
+
end
|
128
|
+
|
129
|
+
# What are we going to return?
|
130
|
+
case options[:type]
|
131
|
+
when :stdout
|
132
|
+
stdout
|
133
|
+
when :stderr
|
134
|
+
stderr
|
135
|
+
else
|
136
|
+
{ :stdout => stdout, :stderr => stderr }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sanitize a filename - taken from http://guides.rubyonrails.org/security.html
|
141
|
+
#
|
142
|
+
# @param filename [String]
|
143
|
+
# @return [String]
|
144
|
+
def self.sanitize_filename(filename)
|
145
|
+
filename.strip.tap do |name|
|
146
|
+
name.sub!(/\A.*(\\|\/)/, '')
|
147
|
+
# Finally, replace all non alphanumeric, underscore
|
148
|
+
# or periods with underscore
|
149
|
+
name.gsub!(/[^\w\.\-]/, '_')
|
150
|
+
end
|
151
|
+
|
152
|
+
filename
|
153
|
+
end
|
154
|
+
|
155
|
+
|
156
|
+
# Used to generate SSH keys for hosts but is completely generic
|
157
|
+
#
|
158
|
+
# @param type [String] the thing that is using the key, this is just here
|
159
|
+
# so this could be used for something other than hosts if needed
|
160
|
+
# @param id [Integer]
|
161
|
+
# @param name [String]
|
162
|
+
def self.generate_ssh_key(type, id, name)
|
163
|
+
key = SSHKey.generate(:comment => "minicron public key for #{name}")
|
164
|
+
|
165
|
+
# Set the locations to save the public key private key pair
|
166
|
+
private_key_path = sanitize_filename(File.expand_path("~/.ssh/minicron_#{type}_#{id}_rsa"))
|
167
|
+
public_key_path = sanitize_filename(File.expand_path("~/.ssh/minicron_#{type}_#{id}_rsa.pub"))
|
168
|
+
|
169
|
+
# Save the public key private key pair
|
170
|
+
File.write(private_key_path, key.private_key)
|
171
|
+
File.write(public_key_path, key.ssh_public_key)
|
172
|
+
|
173
|
+
# Set the correct permissions on the files
|
174
|
+
File.chmod(0600, private_key_path)
|
175
|
+
File.chmod(0644, public_key_path)
|
176
|
+
|
177
|
+
key
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
require 'minicron/alert/email'
|
2
|
+
require 'minicron/alert/sms'
|
3
|
+
require 'minicron/alert/pagerduty'
|
4
|
+
require 'minicron/hub/models/alert'
|
5
|
+
require 'minicron/hub/models/job'
|
6
|
+
|
7
|
+
module Minicron
|
8
|
+
class Alert
|
9
|
+
# Send an alert using all enabled mediums
|
10
|
+
#
|
11
|
+
# @option options [String] kind 'fail' or 'miss'
|
12
|
+
# @option options [Integer, nil] schedule_id only applies to 'miss' alerts
|
13
|
+
# @option options [Integer, nil] execution_id only used by 'fail' alerts
|
14
|
+
# @option options [Integer] job_id used by the #send method
|
15
|
+
# @option options [Time] expected_at only applies to 'miss' alerts
|
16
|
+
def send_all(options = {})
|
17
|
+
Minicron.config['alerts'].each do |medium, value|
|
18
|
+
# Check if the medium is enabled and alert hasn't already been sent
|
19
|
+
if value['enabled'] && !sent?(options)
|
20
|
+
send(
|
21
|
+
:kind => options[:kind],
|
22
|
+
:schedule_id => options[:schedule_id],
|
23
|
+
:execution_id => options[:execution_id],
|
24
|
+
:job_id => options[:job_id],
|
25
|
+
:expected_at => options[:expected_at],
|
26
|
+
:medium => medium
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Send an individual alert
|
33
|
+
#
|
34
|
+
# @option options [String] kind 'fail' or 'miss'
|
35
|
+
# @option options [Integer, nil] schedule_id only applies to 'miss' alerts
|
36
|
+
# @option options [Integer, nil] execution_id only used by 'fail' alerts
|
37
|
+
# @option options [Integer] job_id used to look up the job name for the alert message
|
38
|
+
# @option options [Time] expected_at when the schedule was expected to execute
|
39
|
+
# @option options [String] medium the medium to send the alert via
|
40
|
+
def send(options = {})
|
41
|
+
# Look up the job for this schedule
|
42
|
+
options[:job] = Minicron::Hub::Job.find(options[:job_id])
|
43
|
+
|
44
|
+
# Switch the medium that the alert will be sent via
|
45
|
+
case options[:medium]
|
46
|
+
when 'email'
|
47
|
+
send_email(options)
|
48
|
+
when 'sms'
|
49
|
+
send_sms(options)
|
50
|
+
when 'pagerduty'
|
51
|
+
send_pagerduty(options)
|
52
|
+
else
|
53
|
+
raise Exception, "The medium '#{options[:medium]}' is not supported!"
|
54
|
+
end
|
55
|
+
|
56
|
+
# Store that we sent the alert
|
57
|
+
Minicron::Hub::Alert.create(
|
58
|
+
:schedule_id => options[:schedule_id],
|
59
|
+
:execution_id => options[:execution_id],
|
60
|
+
:kind => options[:kind],
|
61
|
+
:expected_at => options[:expected_at],
|
62
|
+
:medium => options[:medium],
|
63
|
+
:sent_at => Time.now.utc
|
64
|
+
)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Send an email alert, this has the same options as #send
|
68
|
+
def send_email(options = {})
|
69
|
+
email = Minicron::Email.new
|
70
|
+
email.send(
|
71
|
+
Minicron.config['alerts']['email']['from'],
|
72
|
+
Minicron.config['alerts']['email']['to'],
|
73
|
+
"minicron alert for job '#{options[:job].name}'!",
|
74
|
+
email.get_message(options)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
# Send an sms alert, this has the same options as #send
|
79
|
+
def send_sms(options = {})
|
80
|
+
sms = Minicron::SMS.new
|
81
|
+
sms.send(
|
82
|
+
Minicron.config['alerts']['sms']['from'],
|
83
|
+
Minicron.config['alerts']['sms']['to'],
|
84
|
+
sms.get_message(options)
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Send a pagerduty alert, this has the same options as #send
|
89
|
+
def send_pagerduty(options = {})
|
90
|
+
pagerduty = Minicron::PagerDuty.new
|
91
|
+
pagerduty.send(
|
92
|
+
options[:kind] == 'fail' ? 'Job failed!' : 'Job missed!',
|
93
|
+
pagerduty.get_message(options)
|
94
|
+
)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Queries the database to determine if an alert for this kind has already
|
98
|
+
# been sent
|
99
|
+
#
|
100
|
+
# @option options [String] kind 'fail' or 'miss'
|
101
|
+
# @option options [Integer, nil] schedule_id only applies to 'miss' alerts
|
102
|
+
# @option options [Integer, nil] execution_id only used by 'fail' alerts
|
103
|
+
# @option options [Time] expected_at when the schedule was expected to execute
|
104
|
+
# @option options [String] medium the medium to send the alert via
|
105
|
+
def sent?(options = {})
|
106
|
+
Minicron::Hub::Alert.exists?(
|
107
|
+
:kind => options[:kind],
|
108
|
+
:schedule_id => options[:schedule_id],
|
109
|
+
:execution_id => options[:execution_id],
|
110
|
+
:expected_at => options[:expected_at],
|
111
|
+
:medium => options[:medium]
|
112
|
+
)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|