minicron 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'mail'
|
|
2
|
+
|
|
3
|
+
module Minicron
|
|
4
|
+
class Email
|
|
5
|
+
# Configure the mail client
|
|
6
|
+
def initialize
|
|
7
|
+
Mail.defaults do
|
|
8
|
+
delivery_method(
|
|
9
|
+
:smtp,
|
|
10
|
+
:address => Minicron.config['alerts']['email']['smtp']['address'],
|
|
11
|
+
:port => Minicron.config['alerts']['email']['smtp']['port']
|
|
12
|
+
)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
# Return the message for an alert
|
|
16
|
+
#
|
|
17
|
+
# @option options [Minicron::Hub::Job] job a job instance
|
|
18
|
+
# @option options [String] kind 'fail' or 'miss'
|
|
19
|
+
# @option options [Integer, nil] schedule_id only applies to 'miss' alerts
|
|
20
|
+
# @option options [Integer, nil] execution_id only used by 'fail' alerts
|
|
21
|
+
# @option options [Integer] job_id used to look up the job name for the alert message
|
|
22
|
+
# @option options [Time] expected_at when the schedule was expected to execute
|
|
23
|
+
# @option options [String] medium the medium to send the alert via
|
|
24
|
+
def get_message(options = {})
|
|
25
|
+
case options[:kind]
|
|
26
|
+
when 'miss'
|
|
27
|
+
"Job ##{options[:job_id]} '#{options[:job].name}' failed to execute at its expected time: #{options[:expected_at]}."
|
|
28
|
+
when 'fail'
|
|
29
|
+
"Execution ##{options[:execution_id]} of Job ##{options[:job_id]} '#{options[:job].name}' failed."
|
|
30
|
+
else
|
|
31
|
+
raise Exception, "The kind '#{options[:kind]} is not supported!"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Send an email alert
|
|
36
|
+
#
|
|
37
|
+
# @param from [String]
|
|
38
|
+
# @param to [String]
|
|
39
|
+
# @param subject [String]
|
|
40
|
+
# @param message [String]
|
|
41
|
+
def send(from, to, subject, message)
|
|
42
|
+
Mail.deliver do
|
|
43
|
+
to to
|
|
44
|
+
from from
|
|
45
|
+
subject subject
|
|
46
|
+
body message
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require 'pagerduty'
|
|
2
|
+
|
|
3
|
+
module Minicron
|
|
4
|
+
class PagerDuty
|
|
5
|
+
# Used to set up on the pagerduty client
|
|
6
|
+
def initialize
|
|
7
|
+
# Get an instance of the Pagerduty client
|
|
8
|
+
@client = ::Pagerduty.new(Minicron.config['alerts']['pagerduty']['service_key'])
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Return the message for an alert
|
|
12
|
+
#
|
|
13
|
+
# @option options [Minicron::Hub::Job] job a job instance
|
|
14
|
+
# @option options [String] kind 'fail' or 'miss'
|
|
15
|
+
# @option options [Integer, nil] schedule_id only applies to 'miss' alerts
|
|
16
|
+
# @option options [Integer, nil] execution_id only used by 'fail' alerts
|
|
17
|
+
# @option options [Integer] job_id used to look up the job name for the alert message
|
|
18
|
+
# @option options [Time] expected_at when the schedule was expected to execute
|
|
19
|
+
# @option options [String] medium the medium to send the alert via
|
|
20
|
+
def get_message(options = {})
|
|
21
|
+
case options[:kind]
|
|
22
|
+
when 'miss'
|
|
23
|
+
"Job ##{options[:job_id]} failed to execute at its expected time - #{options[:expected_at]}"
|
|
24
|
+
when 'fail'
|
|
25
|
+
"Execution ##{options[:execution_id]} of Job ##{options[:job_id]} failed"
|
|
26
|
+
else
|
|
27
|
+
raise Exception, "The kind '#{options[:kind]} is not supported!"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Send a pager duty alert
|
|
32
|
+
#
|
|
33
|
+
# @param title [String]
|
|
34
|
+
# @param message [String]
|
|
35
|
+
def send(title, message)
|
|
36
|
+
@client.trigger(title, { :message => message })
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
require 'twilio-ruby'
|
|
2
|
+
|
|
3
|
+
module Minicron
|
|
4
|
+
class SMS
|
|
5
|
+
# Used to set up on the twilio client
|
|
6
|
+
def initialize
|
|
7
|
+
# Get an instance of the twilio client
|
|
8
|
+
@client = Twilio::REST::Client.new(
|
|
9
|
+
Minicron.config['alerts']['sms']['twilio']['account_sid'],
|
|
10
|
+
Minicron.config['alerts']['sms']['twilio']['auth_token']
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Return the message for an alert
|
|
15
|
+
#
|
|
16
|
+
# @option options [Minicron::Hub::Job] job a job instance
|
|
17
|
+
# @option options [String] kind 'fail' or 'miss'
|
|
18
|
+
# @option options [Integer, nil] schedule_id only applies to 'miss' alerts
|
|
19
|
+
# @option options [Integer, nil] execution_id only used by 'fail' alerts
|
|
20
|
+
# @option options [Integer] job_id used to look up the job name for the alert message
|
|
21
|
+
# @option options [Time] expected_at when the schedule was expected to execute
|
|
22
|
+
# @option options [String] medium the medium to send the alert via
|
|
23
|
+
def get_message(options = {})
|
|
24
|
+
case options[:kind]
|
|
25
|
+
when 'miss'
|
|
26
|
+
"minicron alert - job missed!\nJob ##{options[:job_id]} failed to execute at its expected time: #{options[:expected_at]}"
|
|
27
|
+
when 'fail'
|
|
28
|
+
"minicron alert - job failed!\nExecution ##{options[:execution_id]} of Job ##{options[:job_id]} failed"
|
|
29
|
+
else
|
|
30
|
+
raise Exception, "The kind '#{options[:kind]} is not supported!"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Send an sms alert
|
|
35
|
+
#
|
|
36
|
+
# @param from [String]
|
|
37
|
+
# @param to [String]
|
|
38
|
+
# @param message [String]
|
|
39
|
+
def send(from, to, message)
|
|
40
|
+
@client.account.messages.create(
|
|
41
|
+
:from => from,
|
|
42
|
+
:to => to,
|
|
43
|
+
:body => message
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
data/lib/minicron/cli.rb
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
require 'pty'
|
|
2
|
+
require 'English'
|
|
3
|
+
require 'rainbow/ext/string'
|
|
4
|
+
require 'commander'
|
|
5
|
+
require 'minicron/constants'
|
|
6
|
+
require 'minicron/transport'
|
|
7
|
+
require 'minicron/transport/client'
|
|
8
|
+
require 'minicron/transport/server'
|
|
9
|
+
require 'minicron/monitor'
|
|
10
|
+
|
|
11
|
+
include Commander::UI
|
|
12
|
+
|
|
13
|
+
module Minicron
|
|
14
|
+
class CLI
|
|
15
|
+
# Function to the parse the config of the options passed to commands
|
|
16
|
+
#
|
|
17
|
+
# @param opts [Hash] The Commander provided options hash
|
|
18
|
+
def parse_config(opts)
|
|
19
|
+
# Parse the --config file options if it was passed
|
|
20
|
+
Minicron.parse_file_config(opts.config)
|
|
21
|
+
|
|
22
|
+
# Parse the cli options
|
|
23
|
+
Minicron.parse_cli_config(
|
|
24
|
+
'global' => {
|
|
25
|
+
'verbose' => opts.verbose,
|
|
26
|
+
'trace' => opts.trace
|
|
27
|
+
},
|
|
28
|
+
'cli' => {
|
|
29
|
+
'mode' => opts.mode,
|
|
30
|
+
'dry_run' => opts.dry_run
|
|
31
|
+
},
|
|
32
|
+
'server' => {
|
|
33
|
+
'host' => opts.host,
|
|
34
|
+
'port' => opts.port,
|
|
35
|
+
'path' => opts.path
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Used as a helper for yielding command output, returns it in a structured hash
|
|
41
|
+
#
|
|
42
|
+
# @param type [Symbol] The type of command output, currently one of :status, :command and :verbose
|
|
43
|
+
# @param output [String]
|
|
44
|
+
# @return [Hash]
|
|
45
|
+
def structured(type, output)
|
|
46
|
+
{ :type => type, :output => output }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Sets up an instance of commander and runs it based on the argv param
|
|
50
|
+
#
|
|
51
|
+
# @param argv [Array] an array of arguments passed to the cli
|
|
52
|
+
# @yieldparam output [String] output from the cli
|
|
53
|
+
# @raise [ArgumentError] if no arguments are passed to the run cli command
|
|
54
|
+
# i.e when the argv param is ['run']. A second option (the command to execute)
|
|
55
|
+
# should be present in the array
|
|
56
|
+
def run(argv)
|
|
57
|
+
# replace ARGV with the contents of argv to aid testability
|
|
58
|
+
ARGV.replace(argv)
|
|
59
|
+
|
|
60
|
+
# Get an instance of commander
|
|
61
|
+
@cli = Commander::Runner.new
|
|
62
|
+
|
|
63
|
+
# Set some default otions on it
|
|
64
|
+
setup_cli
|
|
65
|
+
|
|
66
|
+
# Add the run command to the cli
|
|
67
|
+
add_run_cli_command { |output| yield output }
|
|
68
|
+
|
|
69
|
+
# Add the server command to the cli
|
|
70
|
+
add_server_cli_command
|
|
71
|
+
|
|
72
|
+
# Add the db command to the cli
|
|
73
|
+
add_db_cli_command
|
|
74
|
+
|
|
75
|
+
# And off we go!
|
|
76
|
+
@cli.run!
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Executes a command in a pseudo terminal and yields the output
|
|
80
|
+
#
|
|
81
|
+
# @param command [String] the command to execute e.g 'ls'
|
|
82
|
+
# @option options [String] mode ('line') the method to yield the
|
|
83
|
+
# command output. Either 'line' by line or 'char' by char.
|
|
84
|
+
# @option options [Boolean] verbose whether or not to output extra
|
|
85
|
+
# information for debugging purposes.
|
|
86
|
+
# @yieldparam output [String] output from the command execution
|
|
87
|
+
def run_command(command, options = {})
|
|
88
|
+
# Default the options
|
|
89
|
+
options[:mode] ||= 'line'
|
|
90
|
+
options[:verbose] ||= false
|
|
91
|
+
|
|
92
|
+
# Record the start time of the command
|
|
93
|
+
start = Time.now.utc
|
|
94
|
+
subtract_total = 0
|
|
95
|
+
|
|
96
|
+
# yield the start time
|
|
97
|
+
subtract = Time.now.utc
|
|
98
|
+
yield structured :status, "START #{start.strftime("%Y-%m-%d %H:%M:%S")}"
|
|
99
|
+
subtract_total += Time.now.utc - subtract
|
|
100
|
+
|
|
101
|
+
# Spawn a process to run the command
|
|
102
|
+
begin
|
|
103
|
+
PTY.spawn(command) do |stdout, stdin, pid|
|
|
104
|
+
|
|
105
|
+
# Output some debug info
|
|
106
|
+
if options[:verbose]
|
|
107
|
+
subtract = Time.now.utc
|
|
108
|
+
yield structured :verbose, '[minicron]'.colour(:magenta)
|
|
109
|
+
yield structured :verbose, ' started running '.colour(:blue) + "`#{command}`".colour(:yellow) + " at #{start}\n\n".colour(:blue)
|
|
110
|
+
subtract_total += Time.now.utc - subtract
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
begin
|
|
114
|
+
# Loop until data is no longer being sent to stdout
|
|
115
|
+
until stdout.eof?
|
|
116
|
+
# One character at a time or one line at a time?
|
|
117
|
+
output = options[:mode] == 'char' ? stdout.read(1) : stdout.readline
|
|
118
|
+
|
|
119
|
+
subtract = Time.now.utc
|
|
120
|
+
yield structured :command, output
|
|
121
|
+
subtract_total += Time.now.utc - subtract
|
|
122
|
+
end
|
|
123
|
+
# See https://github.com/ruby/ruby/blob/57fb2199059cb55b632d093c2e64c8a3c60acfbb/ext/pty/pty.c#L519
|
|
124
|
+
rescue Errno::EIO
|
|
125
|
+
ensure
|
|
126
|
+
# Force waiting for the process to finish so we can get the exit status
|
|
127
|
+
Process.wait pid
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
rescue Errno::ENOENT
|
|
131
|
+
exit_status = 1
|
|
132
|
+
|
|
133
|
+
fail Exception, "Running the command `#{command}` failed, are you sure it exists?"
|
|
134
|
+
ensure
|
|
135
|
+
# Record the time the command finished
|
|
136
|
+
finish = Time.now.utc - subtract_total
|
|
137
|
+
exit_status = $CHILD_STATUS.exitstatus ? $CHILD_STATUS.exitstatus : nil
|
|
138
|
+
|
|
139
|
+
# yield the finish time and exit status
|
|
140
|
+
yield structured :status, "FINISH #{finish.strftime("%Y-%m-%d %H:%M:%S")}"
|
|
141
|
+
yield structured :status, "EXIT #{exit_status}"
|
|
142
|
+
|
|
143
|
+
# Output some debug info
|
|
144
|
+
if options[:verbose]
|
|
145
|
+
yield structured :verbose, "\n" + "[minicron]".colour(:magenta)
|
|
146
|
+
yield structured :verbose, ' finished running '.colour(:blue) + "`#{command}`".colour(:yellow) + " at #{start}\n".colour(:blue)
|
|
147
|
+
yield structured :verbose, '[minicron]'.colour(:magenta)
|
|
148
|
+
yield structured :verbose, ' running '.colour(:blue) + "`#{command}`".colour(:yellow) + " took #{finish - start}s\n".colour(:blue)
|
|
149
|
+
yield structured :verbose, '[minicron]'.colour(:magenta)
|
|
150
|
+
yield structured :verbose, " `#{command}`".colour(:yellow) + ' finished with an exit status of '.colour(:blue)
|
|
151
|
+
yield structured :verbose, exit_status == 0 ? "#{exit_status}\n".colour(:green) : "#{exit_status}\n".colour(:red)
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Whether or not coloured output of the rainbox gem is enabled, this is
|
|
157
|
+
# enabled by default
|
|
158
|
+
#
|
|
159
|
+
# @return [Boolean] whether rainbow is enabled or not
|
|
160
|
+
def coloured_output?
|
|
161
|
+
Rainbow.enabled
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Enable coloured terminal output from the rainbow gem, this is enabled
|
|
165
|
+
# by default
|
|
166
|
+
#
|
|
167
|
+
# @return [Boolean] whether rainbow is enabled or not
|
|
168
|
+
def enable_coloured_output!
|
|
169
|
+
Rainbow.enabled = true
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Disable coloured terminal output from the rainbow gem, this is enabled
|
|
173
|
+
# by default
|
|
174
|
+
#
|
|
175
|
+
# @return [Boolean] whether rainbow is enabled or not
|
|
176
|
+
def disable_coloured_output!
|
|
177
|
+
Rainbow.enabled = false
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
# Sets the basic options for a commander cli instance
|
|
183
|
+
def setup_cli
|
|
184
|
+
# basic information for the help menu
|
|
185
|
+
@cli.program :name, 'minicron'
|
|
186
|
+
@cli.program :help, 'Author', 'James White <dev.jameswhite+minicron@gmail.com>'
|
|
187
|
+
@cli.program :help, 'License', 'GPL v3'
|
|
188
|
+
@cli.program :version, Minicron::VERSION
|
|
189
|
+
@cli.program :description, 'cli for minicron; a system a to manage and monitor cron jobs'
|
|
190
|
+
|
|
191
|
+
# Set the default command to run
|
|
192
|
+
@cli.default_command :help
|
|
193
|
+
|
|
194
|
+
# Check if --trace was pased or not
|
|
195
|
+
if @cli.instance_variable_get(:@args).include? '--trace'
|
|
196
|
+
Minicron.config['global']['trace'] = true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Add a global option for verbose mode
|
|
200
|
+
@cli.global_option '--verbose', "Turn on verbose mode. Default: #{Minicron.config['cli']['verbose']}" do
|
|
201
|
+
Minicron.config['global']['verbose'] = true
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# Add a global option for passing the path to a config file
|
|
205
|
+
@cli.global_option '--config FILE', 'Set the config file to use'
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Setup a job by sending the SETUP command to the server
|
|
209
|
+
#
|
|
210
|
+
# @param command [String] the job command
|
|
211
|
+
# @param faye a faye client instance
|
|
212
|
+
# @return [Hash] the job_id and execution_id
|
|
213
|
+
def setup_job(command, faye)
|
|
214
|
+
# Get the fully qualified domain name of the currnet host
|
|
215
|
+
fqdn = `hostname -f`.strip
|
|
216
|
+
|
|
217
|
+
# Get the short hostname of the current host
|
|
218
|
+
hostname = `hostname -s`.strip
|
|
219
|
+
|
|
220
|
+
# Get the md5 hash for the job
|
|
221
|
+
job_hash = Minicron::Transport.get_job_hash(command, fqdn)
|
|
222
|
+
|
|
223
|
+
# Fire up eventmachine
|
|
224
|
+
faye.ensure_em_running
|
|
225
|
+
|
|
226
|
+
# Setup the job on the server
|
|
227
|
+
ids = faye.setup(job_hash, command, fqdn, hostname)
|
|
228
|
+
|
|
229
|
+
# Wait until we get the execution id
|
|
230
|
+
faye.ensure_delivery
|
|
231
|
+
|
|
232
|
+
# Return the ids
|
|
233
|
+
ids
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
# Add the `minicron db` command
|
|
237
|
+
def add_db_cli_command
|
|
238
|
+
@cli.command :db do |c|
|
|
239
|
+
c.syntax = 'minicron db [setup|dump]'
|
|
240
|
+
c.description = 'Loads or dumps the minicron database schema.'
|
|
241
|
+
|
|
242
|
+
c.action do |args, opts|
|
|
243
|
+
# Check that exactly one argument has been passed
|
|
244
|
+
if args.length != 1
|
|
245
|
+
fail ArgumentError, 'A valid command to run is required! See `minicron help db`'
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Parse the file and cli config options
|
|
249
|
+
parse_config(opts)
|
|
250
|
+
|
|
251
|
+
# These are inlined as we only need them in this use case
|
|
252
|
+
require 'rake'
|
|
253
|
+
require 'minicron/hub/app'
|
|
254
|
+
require 'sinatra/activerecord/rake'
|
|
255
|
+
|
|
256
|
+
# Setup the db
|
|
257
|
+
Minicron::Hub::App.setup_db
|
|
258
|
+
|
|
259
|
+
# Tell activerecord where the db folder is, it assumes it is in db/
|
|
260
|
+
Sinatra::ActiveRecordTasks.db_dir = 'lib/minicron/hub/db'
|
|
261
|
+
|
|
262
|
+
# Adjust the task name
|
|
263
|
+
task = args.first == 'setup' ? 'load' : args.first
|
|
264
|
+
|
|
265
|
+
# Run the task
|
|
266
|
+
Rake.application['db:schema:' + task].invoke
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Add the `minicron server` command
|
|
272
|
+
def add_server_cli_command
|
|
273
|
+
@cli.command :server do |c|
|
|
274
|
+
c.syntax = 'minicron server'
|
|
275
|
+
c.description = 'Starts the minicron server.'
|
|
276
|
+
c.option '--host STRING', String, "The host for the server to listen on. Default: #{Minicron.config['server']['host']}"
|
|
277
|
+
c.option '--port STRING', Integer, "How port for the server to listed on. Default: #{Minicron.config['server']['port']}"
|
|
278
|
+
c.option '--path STRING', String, "The path on the host. Default: #{Minicron.config['server']['path']}"
|
|
279
|
+
|
|
280
|
+
c.action do |args, opts|
|
|
281
|
+
# Parse the file and @cli config options
|
|
282
|
+
parse_config(opts)
|
|
283
|
+
|
|
284
|
+
# Run the execution monitor (this runs in a separate thread)
|
|
285
|
+
monitor = Minicron::Monitor.new
|
|
286
|
+
monitor.start!
|
|
287
|
+
|
|
288
|
+
# Start the server!
|
|
289
|
+
server = Minicron::Transport::Server.new
|
|
290
|
+
server.start!(
|
|
291
|
+
Minicron.config['server']['host'],
|
|
292
|
+
Minicron.config['server']['port'],
|
|
293
|
+
Minicron.config['server']['path']
|
|
294
|
+
)
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Add the `minicron run [command]` command
|
|
300
|
+
# @yieldparam output [String] output from the cli
|
|
301
|
+
def add_run_cli_command
|
|
302
|
+
# Add the run command to the cli
|
|
303
|
+
@cli.command :run do |c|
|
|
304
|
+
c.syntax = "minicron run 'command -option value'"
|
|
305
|
+
c.description = 'Runs the command passed as an argument.'
|
|
306
|
+
c.option '--mode STRING', String, "How to capture the command output, each 'line' or each 'char'? Default: #{Minicron.config['cli']['mode']}"
|
|
307
|
+
c.option '--dry-run', "Run the command without sending the output to the server. Default: #{Minicron.config['cli']['dry_run']}"
|
|
308
|
+
|
|
309
|
+
c.action do |args, opts|
|
|
310
|
+
# Check that exactly one argument has been passed
|
|
311
|
+
if args.length != 1
|
|
312
|
+
fail ArgumentError, 'A valid command to run is required! See `minicron help run`'
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
# Parse the file and cli config options
|
|
316
|
+
parse_config(opts)
|
|
317
|
+
|
|
318
|
+
begin
|
|
319
|
+
# Set up the job and get the job and execution ids
|
|
320
|
+
unless Minicron.config['cli']['dry_run']
|
|
321
|
+
# Get a faye instance so we can send data about the job
|
|
322
|
+
faye = Minicron::Transport::Client.new(
|
|
323
|
+
Minicron.config['client']['scheme'],
|
|
324
|
+
Minicron.config['client']['host'],
|
|
325
|
+
Minicron.config['client']['port'],
|
|
326
|
+
Minicron.config['client']['path']
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# Set up the job and get the jexecution and job ids back from the server
|
|
330
|
+
ids = setup_job(args.first, faye)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# Execute the command and yield the output
|
|
334
|
+
run_command(args.first, :mode => Minicron.config['cli']['mode'], :verbose => Minicron.config['global']['verbose']) do |output|
|
|
335
|
+
# We need to handle the yielded output differently based on it's type
|
|
336
|
+
case output[:type]
|
|
337
|
+
when :status
|
|
338
|
+
unless Minicron.config['cli']['dry_run']
|
|
339
|
+
faye.send(:job_id => ids[:job_id], :execution_id => ids[:execution_id], :type => :status, :message => output[:output])
|
|
340
|
+
end
|
|
341
|
+
when :command
|
|
342
|
+
unless Minicron.config['cli']['dry_run']
|
|
343
|
+
faye.send(:job_id => ids[:job_id], :execution_id => ids[:execution_id], :type => :output, :message => output[:output])
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
yield output[:output] unless output[:type] == :status
|
|
348
|
+
end
|
|
349
|
+
rescue Exception => e
|
|
350
|
+
# Send the exception message to the server and yield it
|
|
351
|
+
unless Minicron.config['cli']['dry_run']
|
|
352
|
+
faye.send(:job_id => ids[:job_id], :execution_id => ids[:execution_id], :type => :output, :message => e.message)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
fail e
|
|
356
|
+
ensure
|
|
357
|
+
# Ensure that all messages are delivered and that we
|
|
358
|
+
unless Minicron.config['cli']['dry_run']
|
|
359
|
+
faye.ensure_delivery
|
|
360
|
+
faye.tidy_up
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
end
|
|
366
|
+
end
|
|
367
|
+
end
|