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
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Minicron
|
4
|
+
# Used to interact with the crontab on hosts over an ssh connection
|
5
|
+
# TODO: I've had a moment of clarity, I don't need to do all the CRUD
|
6
|
+
# using unix commands. I can cat the crontab, manipulate it in ruby
|
7
|
+
# and then echo it back!
|
8
|
+
class Cron
|
9
|
+
# Initialise the cron class
|
10
|
+
#
|
11
|
+
# @param ssh [Minicron::Transport::SSH] instance
|
12
|
+
def initialize(ssh)
|
13
|
+
@ssh = ssh
|
14
|
+
end
|
15
|
+
|
16
|
+
# Escape the quotes in a command
|
17
|
+
#
|
18
|
+
# @param command [String]
|
19
|
+
# @return [String]
|
20
|
+
def escape_command(command)
|
21
|
+
command.gsub(/\\|'/) { |c| "\\#{c}" }
|
22
|
+
end
|
23
|
+
|
24
|
+
# Build the minicron command to be used in the crontab
|
25
|
+
#
|
26
|
+
# @param command [String]
|
27
|
+
# @param schedule [String]
|
28
|
+
# @return [String]
|
29
|
+
def build_minicron_command(command, schedule)
|
30
|
+
# Escape any quotes in the command
|
31
|
+
command = escape_command(command)
|
32
|
+
|
33
|
+
"#{schedule} root minicron run '#{command}'"
|
34
|
+
end
|
35
|
+
|
36
|
+
# Used to find a string and replace it with another in the crontab by
|
37
|
+
# using the sed command
|
38
|
+
#
|
39
|
+
# @param conn an instance of an open ssh connection
|
40
|
+
# @param find [String]
|
41
|
+
# @param replace [String]
|
42
|
+
def find_and_replace(conn, find, replace)
|
43
|
+
# TODO: move ssh test here for crontab permissions
|
44
|
+
|
45
|
+
# Get the full crontab
|
46
|
+
crontab = conn.exec!('cat /etc/crontab').to_s.strip
|
47
|
+
|
48
|
+
# Replace the full string with the replacement string
|
49
|
+
begin
|
50
|
+
crontab[find] = replace
|
51
|
+
rescue Exception => e
|
52
|
+
raise Exception, "Unable to replace '#{find}' with '#{replace}' in the crontab, reason: #{e}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Echo the crontab back to the tmp crontab
|
56
|
+
update = conn.exec!("echo #{crontab.shellescape} > /etc/crontab.tmp && echo 'y' || echo 'n'").to_s.strip
|
57
|
+
|
58
|
+
# Throw an exception if it failed
|
59
|
+
if update != 'y'
|
60
|
+
raise Exception, "Unable to replace '#{find}' with '#{replace}' in the crontab"
|
61
|
+
end
|
62
|
+
|
63
|
+
# If it's a delete
|
64
|
+
if replace == ''
|
65
|
+
# Check the original line is no longer there
|
66
|
+
grep = conn.exec!("grep -F \"#{find}\" /etc/crontab.tmp").to_s.strip
|
67
|
+
|
68
|
+
# Throw an exception if we can't see our new line at the end of the file
|
69
|
+
if grep != replace
|
70
|
+
raise Exception, "Expected to find nothing when grepping crontab but found #{grep}"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
# Check the updated line is there
|
74
|
+
grep = conn.exec!("grep -F \"#{replace}\" /etc/crontab.tmp").to_s.strip
|
75
|
+
|
76
|
+
# Throw an exception if we can't see our new line at the end of the file
|
77
|
+
if grep != replace
|
78
|
+
raise Exception, "Expected to find '#{replace}' when grepping crontab but found #{grep}"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# And finally replace the crontab with the new one now we now the change worked
|
83
|
+
move = conn.exec!("mv /etc/crontab.tmp /etc/crontab && echo 'y' || echo 'n'").to_s.strip
|
84
|
+
|
85
|
+
if move != 'y'
|
86
|
+
raise Exception, 'Unable to move tmp crontab with updated crontab'
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Add the schedule for this job to the crontab
|
91
|
+
#
|
92
|
+
# @param job [Minicron::Hub::Job] an instance of a job model
|
93
|
+
# @param schedule [String] the job schedule as a string
|
94
|
+
# @param conn an instance of an open ssh connection
|
95
|
+
def add_schedule(job, schedule, conn = nil)
|
96
|
+
# Open an SSH connection
|
97
|
+
conn ||= @ssh.open
|
98
|
+
|
99
|
+
# Prepare the line we are going to write to the crontab
|
100
|
+
line = build_minicron_command(job.command, schedule)
|
101
|
+
echo_line = "echo \"#{line}\" >> /etc/crontab && echo 'y' || echo 'n'"
|
102
|
+
|
103
|
+
# Append it to the end of the crontab
|
104
|
+
write = conn.exec!(echo_line).strip
|
105
|
+
|
106
|
+
# Throw an exception if it failed
|
107
|
+
if write != 'y'
|
108
|
+
raise Exception, "Unable to write '#{line}' to the crontab"
|
109
|
+
end
|
110
|
+
|
111
|
+
# Check the line is there
|
112
|
+
tail = conn.exec!('tail -n 1 /etc/crontab').strip
|
113
|
+
|
114
|
+
# Throw an exception if we can't see our new line at the end of the file
|
115
|
+
if tail != line
|
116
|
+
raise Exception, "Expected to find '#{line}' at eof but found '#{tail}'"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Update the schedule for this job in the crontab
|
121
|
+
#
|
122
|
+
# @param job [Minicron::Hub::Job] an instance of a job model
|
123
|
+
# @param old_schedule [String] the old job schedule as a string
|
124
|
+
# @param new_schedule [String] the new job schedule as a string
|
125
|
+
# @param conn an instance of an open ssh connection
|
126
|
+
def update_schedule(job, old_schedule, new_schedule, conn = nil)
|
127
|
+
# Open an SSH connection
|
128
|
+
conn ||= @ssh.open
|
129
|
+
|
130
|
+
# We are looking for the current value of the schedule
|
131
|
+
find = build_minicron_command(job.command, old_schedule)
|
132
|
+
|
133
|
+
# And replacing it with the updated value
|
134
|
+
replace = build_minicron_command(job.command, new_schedule)
|
135
|
+
|
136
|
+
# Replace the old schedule with the new schedule
|
137
|
+
find_and_replace(conn, find, replace)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Remove the schedule for this job from the crontab
|
141
|
+
#
|
142
|
+
# @param job [Minicron::Hub::Job] an instance of a job model
|
143
|
+
# @param schedule [String] the job schedule as a string
|
144
|
+
# @param conn an instance of an open ssh connection
|
145
|
+
def delete_schedule(job, schedule, conn = nil)
|
146
|
+
# Open an SSH connection
|
147
|
+
conn ||= @ssh.open
|
148
|
+
|
149
|
+
# We are looking for the current value of the schedule
|
150
|
+
find = build_minicron_command(job.command, schedule)
|
151
|
+
|
152
|
+
# Replace the old schedule with nothing i.e deleting it
|
153
|
+
find_and_replace(conn, find, '')
|
154
|
+
end
|
155
|
+
|
156
|
+
# Delete a job and all it's schedules from the crontab
|
157
|
+
#
|
158
|
+
# @param job [Minicron::Hub::Job] a job instance with it's schedules
|
159
|
+
# @param conn an instance of an open ssh connection
|
160
|
+
def delete_job(job, conn = nil)
|
161
|
+
conn ||= @ssh.open
|
162
|
+
|
163
|
+
# Loop through each schedule and delete them one by one
|
164
|
+
# TODO: share the ssh connection for this so it's faster when
|
165
|
+
# many schedules exist
|
166
|
+
# TODO: what if one schedule removal fails but others don't? Should
|
167
|
+
# we try and rollback somehow or just return the job with half its
|
168
|
+
# schedules deleted?
|
169
|
+
job.schedules.each do |schedule|
|
170
|
+
delete_schedule(job, schedule.formatted, conn)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
# Delete a host and all it's jobs from the crontab
|
175
|
+
#
|
176
|
+
# @param job [Minicron::Hub::Job] a job instance with it's schedules
|
177
|
+
# @param conn an instance of an open ssh connection
|
178
|
+
def delete_host(host, conn = nil)
|
179
|
+
conn ||= @ssh.open
|
180
|
+
|
181
|
+
# Loop through each job and delete them one by one
|
182
|
+
# TODO: share the ssh connection for this so it's faster when
|
183
|
+
# many schedules exist
|
184
|
+
# TODO: what if one schedule removal fails but others don't? Should
|
185
|
+
# we try and rollback somehow or just return the job with half its
|
186
|
+
# schedules deleted?
|
187
|
+
host.jobs.each do |job|
|
188
|
+
delete_job(job, conn)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'minicron'
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'sinatra/activerecord'
|
4
|
+
require 'sinatra/assetpack'
|
5
|
+
require 'erubis'
|
6
|
+
require 'oj'
|
7
|
+
|
8
|
+
module Minicron::Hub
|
9
|
+
class App < Sinatra::Base
|
10
|
+
register Sinatra::ActiveRecordExtension
|
11
|
+
register Sinatra::AssetPack
|
12
|
+
|
13
|
+
# Set the application root
|
14
|
+
set :root, Minicron::HUB_PATH
|
15
|
+
|
16
|
+
configure do
|
17
|
+
# Don't log them. We'll do that ourself
|
18
|
+
set :dump_errors, false
|
19
|
+
|
20
|
+
# Don't capture any errors. Throw them up the stack
|
21
|
+
set :raise_errors, true
|
22
|
+
|
23
|
+
# Disable internal middleware for presenting errors
|
24
|
+
# as useful HTML pages
|
25
|
+
set :show_exceptions, false
|
26
|
+
end
|
27
|
+
|
28
|
+
# Configure how we server assets
|
29
|
+
assets do
|
30
|
+
serve '/css', :from => 'assets/css'
|
31
|
+
serve '/js', :from => 'assets/js'
|
32
|
+
serve '/fonts', :from => 'assets/fonts'
|
33
|
+
serve '/app', :from => 'assets/app'
|
34
|
+
|
35
|
+
# Set up the application css
|
36
|
+
css :app, '/css/all.css', [
|
37
|
+
'/css/bootswatch.min.css',
|
38
|
+
'/css/main.css'
|
39
|
+
]
|
40
|
+
|
41
|
+
# Set up the application javascript
|
42
|
+
js :app, '/js/all.js', [
|
43
|
+
# Dependencies, the order of these is important
|
44
|
+
'/js/jquery-2.1.0.min.js',
|
45
|
+
'/js/handlebars-1.3.0.min.js',
|
46
|
+
'/js/ember-1.4.1.min.js',
|
47
|
+
'/js/ember-data-1.0.0-beta.7.f87cba88.min.js',
|
48
|
+
'/js/faye-browser-1.0.1.min.js',
|
49
|
+
'/js/ansi_up-1.1.1.min.js',
|
50
|
+
'/js/bootstrap-3.1.1.min.js',
|
51
|
+
'/js/moment-2.5.1.min.js',
|
52
|
+
|
53
|
+
# Ember application files
|
54
|
+
'/app/**/*.js'
|
55
|
+
]
|
56
|
+
end
|
57
|
+
|
58
|
+
# Called on class initilisation, sets up the database and requires all
|
59
|
+
# the application files
|
60
|
+
def initialize
|
61
|
+
super
|
62
|
+
|
63
|
+
# Initialize the db
|
64
|
+
Minicron::Hub::App.setup_db
|
65
|
+
|
66
|
+
# Load all our model serializers
|
67
|
+
Dir[File.dirname(__FILE__) + '/serializers/*.rb'].each do |serializer|
|
68
|
+
require serializer
|
69
|
+
end
|
70
|
+
|
71
|
+
# Load all our models
|
72
|
+
Dir[File.dirname(__FILE__) + '/models/*.rb'].each do |model|
|
73
|
+
require model
|
74
|
+
end
|
75
|
+
|
76
|
+
# Load all our controllers
|
77
|
+
Dir[File.dirname(__FILE__) + '/controllers/**/*.rb'].each do |controller|
|
78
|
+
require controller
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Used to set up the database connection
|
83
|
+
def self.setup_db
|
84
|
+
# Configure the database
|
85
|
+
case Minicron.config['database']['type']
|
86
|
+
when 'mysql'
|
87
|
+
set :database, {
|
88
|
+
:adapter => 'mysql2',
|
89
|
+
:host => Minicron.config['database']['host'],
|
90
|
+
:database => Minicron.config['database']['database'],
|
91
|
+
:username => Minicron.config['database']['username'],
|
92
|
+
:password => Minicron.config['database']['password']
|
93
|
+
}
|
94
|
+
else
|
95
|
+
raise Exception, "The database #{Minicron.config['database']['type']} is not supported"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Based on http://hawkins.io/2013/06/error-handling-in-sinatra-apis/
|
101
|
+
class ExceptionHandling
|
102
|
+
def initialize(app)
|
103
|
+
@app = app
|
104
|
+
end
|
105
|
+
|
106
|
+
def handle_exception(env, e, status)
|
107
|
+
if Minicron.config['global']['trace']
|
108
|
+
env['rack.errors'].puts(e)
|
109
|
+
env['rack.errors'].puts(e.backtrace.join("\n"))
|
110
|
+
env['rack.errors'].flush
|
111
|
+
end
|
112
|
+
|
113
|
+
# Display the error message
|
114
|
+
hash = { :error => e.to_s }
|
115
|
+
|
116
|
+
# Display the full trace if tracing is enabled
|
117
|
+
hash[:trace] = e.backtrace if Minicron.config['global']['trace']
|
118
|
+
|
119
|
+
[status, { 'Content-Type' => 'application/json' }, [hash.to_json]]
|
120
|
+
end
|
121
|
+
|
122
|
+
def call(env)
|
123
|
+
begin
|
124
|
+
@app.call env
|
125
|
+
rescue ActiveRecord::RecordNotFound => e
|
126
|
+
handle_exception(env, e, 404)
|
127
|
+
rescue Exception => e
|
128
|
+
handle_exception(env, e, 500)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
(function() {
|
4
|
+
window.Minicron = Ember.Application.create({
|
5
|
+
LOG_TRANSITIONS: true,
|
6
|
+
LOG_ACTIVE_GENERATION: true,
|
7
|
+
LOG_VIEW_LOOKUPS: true,
|
8
|
+
LOG_TRANSITIONS_INTERNAL: true
|
9
|
+
});
|
10
|
+
|
11
|
+
// This should be called by all views when they are loaded
|
12
|
+
Minicron.onViewLoad = function() {
|
13
|
+
// Twitter tooltip plugin
|
14
|
+
Ember.$('body').tooltip({
|
15
|
+
selector: '[data-toggle=tooltip]'
|
16
|
+
});
|
17
|
+
};
|
18
|
+
|
19
|
+
// Configure Ember Data so it can find the API
|
20
|
+
Minicron.ApplicationAdapter = DS.RESTAdapter.extend({
|
21
|
+
namespace: 'api',
|
22
|
+
});
|
23
|
+
|
24
|
+
Minicron.ApplicationController = Ember.ArrayController.extend({
|
25
|
+
sortedExecutions: function() {
|
26
|
+
return this.get('content').toArray().sort(function(a, b) {
|
27
|
+
a = +moment(a.get('created_at'));
|
28
|
+
b = +moment(b.get('created_at'));
|
29
|
+
|
30
|
+
if (a > b) {
|
31
|
+
return -1;
|
32
|
+
} else if (a < b) {
|
33
|
+
return 1;
|
34
|
+
}
|
35
|
+
|
36
|
+
return 0;
|
37
|
+
});
|
38
|
+
}.property('content.@each').cacheable()
|
39
|
+
});
|
40
|
+
|
41
|
+
Minicron.ApplicationRoute = Ember.Route.extend({
|
42
|
+
actions: {
|
43
|
+
error: function(error) {
|
44
|
+
console.log(error);
|
45
|
+
|
46
|
+
// Create a new error to be passed as the controller
|
47
|
+
var ember_error = new Ember.Error();
|
48
|
+
|
49
|
+
// Set the details of the error we need
|
50
|
+
ember_error.name = 'Error';
|
51
|
+
ember_error.message = error.responseJSON.error;
|
52
|
+
ember_error.number = error.status;
|
53
|
+
|
54
|
+
this.render('fatal-error', {
|
55
|
+
controller: ember_error
|
56
|
+
});
|
57
|
+
}
|
58
|
+
},
|
59
|
+
model: function() {
|
60
|
+
return this.store.find('execution');
|
61
|
+
},
|
62
|
+
setupController: function(controller, model) {
|
63
|
+
controller.set('application', model);
|
64
|
+
this._super(controller, model);
|
65
|
+
},
|
66
|
+
afterModel: function(model, transition) {
|
67
|
+
var client = new Faye.Client(window.location.protocol + '//' + window.location.host + '/faye'),
|
68
|
+
store = model.store,
|
69
|
+
self = this;
|
70
|
+
window.store = store;
|
71
|
+
|
72
|
+
client.addExtension({
|
73
|
+
incoming: function(message, callback) {
|
74
|
+
// We only care about job messages
|
75
|
+
if (message.channel.substr(1, 3) === 'job') {
|
76
|
+
var segments = message.channel.split('/');
|
77
|
+
var job_id = segments[2];
|
78
|
+
var job_execution_id = segments[3];
|
79
|
+
var type = segments[4];
|
80
|
+
var message_data = message.data.message;
|
81
|
+
|
82
|
+
// TODO: remove this!
|
83
|
+
console.log(job_id, job_execution_id, type, message);
|
84
|
+
|
85
|
+
// Is it a status message?
|
86
|
+
if (type === 'status') {
|
87
|
+
// Is it a setup message
|
88
|
+
if (typeof message_data.action != 'undefined' && message_data.action === 'SETUP') {
|
89
|
+
// The SETUP message is defined slightly differently, segment 2 contains the
|
90
|
+
// job hash and segment 3 contains '*job_id*-*job_execution_id*'
|
91
|
+
var ids = job_execution_id.split('-');
|
92
|
+
job_id = ids[0];
|
93
|
+
job_execution_id = ids[1];
|
94
|
+
|
95
|
+
// Append the job relationship to it
|
96
|
+
store.find('job', job_id).then(function(job) {
|
97
|
+
// Create the execution
|
98
|
+
store.push('execution', {
|
99
|
+
id: job_execution_id,
|
100
|
+
created_at: moment.utc(message.data.ts).format('YYYY-MM-DDTHH:mm:ss[Z]'),
|
101
|
+
job: job
|
102
|
+
}, true);
|
103
|
+
});
|
104
|
+
// Is it a start message?
|
105
|
+
} else if (message_data.substr(0, 5) === 'START') {
|
106
|
+
// Set the execution start time
|
107
|
+
store.find('execution', job_execution_id).then(function(execution) {
|
108
|
+
execution.set('started_at', moment.utc(message_data.substr(6)).format('YYYY-MM-DDTHH:mm:ss[Z]'));
|
109
|
+
});
|
110
|
+
// Is it a finish message?
|
111
|
+
} else if (message_data.substr(0, 6) === 'FINISH') {
|
112
|
+
// Set the execution finish time
|
113
|
+
store.find('execution', job_execution_id).then(function(execution) {
|
114
|
+
execution.set('finished_at', moment.utc(message_data.substr(7)).format('YYYY-MM-DDTHH:mm:ss[Z]'));
|
115
|
+
});
|
116
|
+
// Is it an exit message?
|
117
|
+
} else if (message_data.substr(0, 4) === 'EXIT') {
|
118
|
+
// Set the execution exit status
|
119
|
+
store.find('execution', job_execution_id).then(function(execution) {
|
120
|
+
execution.set('exit_status', +message_data.substr(5));
|
121
|
+
});
|
122
|
+
}
|
123
|
+
// Is it an output message?
|
124
|
+
} else if (type === 'output') {
|
125
|
+
store.find('execution', job_execution_id).then(function(execution) {
|
126
|
+
// Add this bit of job execution output
|
127
|
+
var output = store.createRecord('job_execution_output', {
|
128
|
+
id: message.data.job_execution_output_id,
|
129
|
+
output: message_data,
|
130
|
+
seq: message.data.seq,
|
131
|
+
timestamp: moment(message.data.ts).format()
|
132
|
+
});
|
133
|
+
|
134
|
+
output.set('execution', execution);
|
135
|
+
|
136
|
+
execution.get('job_execution_outputs').then(function(outputs) {
|
137
|
+
outputs.pushObject(output);
|
138
|
+
});
|
139
|
+
});
|
140
|
+
}
|
141
|
+
}
|
142
|
+
|
143
|
+
// We're done with the message, pass it back to Faye
|
144
|
+
callback(message);
|
145
|
+
}
|
146
|
+
});
|
147
|
+
|
148
|
+
client.subscribe('/job/**');
|
149
|
+
}
|
150
|
+
});
|
151
|
+
})();
|