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,280 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function() {
|
|
4
|
+
// Taken from http://stackoverflow.com/a/1830844/483271
|
|
5
|
+
function isNumeric(value) {
|
|
6
|
+
return !isNaN(parseFloat(value)) && isFinite(value);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// Normalise expression values, such as the string equivelants of months/days
|
|
10
|
+
function normaliseExpressionValue(value) {
|
|
11
|
+
var mappings = {
|
|
12
|
+
'jan': '1',
|
|
13
|
+
'feb': '2',
|
|
14
|
+
'mar': '3',
|
|
15
|
+
'apr': '4',
|
|
16
|
+
'may': '5',
|
|
17
|
+
'jun': '6',
|
|
18
|
+
'jul': '7',
|
|
19
|
+
'aug': '8',
|
|
20
|
+
'sep': '9',
|
|
21
|
+
'oct': '10',
|
|
22
|
+
'nov': '11',
|
|
23
|
+
'dec': '12',
|
|
24
|
+
'sun': '0',
|
|
25
|
+
'mon': '1',
|
|
26
|
+
'tue': '2',
|
|
27
|
+
'wed': '3',
|
|
28
|
+
'thu': '4',
|
|
29
|
+
'fri': '5',
|
|
30
|
+
'sat': '6'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
// If it was found in the mappings object return it
|
|
34
|
+
if (typeof mappings[value.toLowerCase()] !== 'undefined') {
|
|
35
|
+
return mappings[value.toLowerCase()];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return value;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Used to parse the schedule on view load and update the GUI with those values
|
|
42
|
+
function parseSchedule(schedule) {
|
|
43
|
+
// Split the schedule into its 5 parts based on whitespace
|
|
44
|
+
var expression = schedule.split(' ');
|
|
45
|
+
|
|
46
|
+
// Loop every value in the expression
|
|
47
|
+
for (var i = 0; i < expression.length; i++) {
|
|
48
|
+
// If the value is '*' we don't need to do anything
|
|
49
|
+
if (expression[i] === '*') {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Select the panel for this expression
|
|
54
|
+
var panel = Ember.$('#schedule-editor').find('.panel').eq(i);
|
|
55
|
+
|
|
56
|
+
// If it's a */n expression
|
|
57
|
+
if (expression[i].substr(0, 2) === '*/' && isNumeric(expression[i].substr(2))) {
|
|
58
|
+
// Show the correct tab
|
|
59
|
+
panel.find('.nav li:eq(1) a').tab('show');
|
|
60
|
+
|
|
61
|
+
// Set the value of the expression in the input box
|
|
62
|
+
panel.find('.tab-content .active input[type="number"]').val(expression[i].substr(2));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If it's an each selected expression
|
|
66
|
+
// TODO: support range expression e.g 10-15
|
|
67
|
+
else if (isNumeric(expression[i]) || expression[i].split(',').length > 1 || expression[i].split('-').length > 1) {
|
|
68
|
+
var value,
|
|
69
|
+
range,
|
|
70
|
+
selected = [],
|
|
71
|
+
split_by_comma = expression[i].split(',');
|
|
72
|
+
|
|
73
|
+
// Show the correct tab
|
|
74
|
+
panel.find('.nav li').last().find('a').tab('show');
|
|
75
|
+
|
|
76
|
+
// If it's just a plain number add it to selected array and we're done
|
|
77
|
+
if (isNumeric(expression[i])) {
|
|
78
|
+
selected.push(expression[i]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// If it's a group of values then we need to split them up and handle them
|
|
82
|
+
else if (split_by_comma.length > 0) {
|
|
83
|
+
for (var j = 0; j < split_by_comma.length; j++) {
|
|
84
|
+
// If it was a range expresssion convert it to a csv format
|
|
85
|
+
if (split_by_comma[j].split('-').length > 1) {
|
|
86
|
+
range = split_by_comma[j].split('-');
|
|
87
|
+
|
|
88
|
+
// Loop every value in the range and add it to the selected array
|
|
89
|
+
for (var pos = parseInt(range[0]); pos <= parseInt(range[1]); pos++) {
|
|
90
|
+
selected.push(pos.toString());
|
|
91
|
+
}
|
|
92
|
+
// If it was just a normal csv expression
|
|
93
|
+
} else {
|
|
94
|
+
selected.push(normaliseExpressionValue(split_by_comma[j]));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Loop every value in the selected array and 'check' it
|
|
100
|
+
panel.find('.tab-content .active input[type="checkbox"]').each(function(k, v) {
|
|
101
|
+
// Get the 'value' of the checkbox
|
|
102
|
+
var $this = Ember.$(this);
|
|
103
|
+
value = $this.data('value');
|
|
104
|
+
|
|
105
|
+
// Should it be checked?
|
|
106
|
+
if (selected.indexOf(value.toString()) >= 0) {
|
|
107
|
+
$this[0].checked = true;
|
|
108
|
+
$this.closest('label').addClass('active');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Finally set the schedule in the schedule input textbox
|
|
115
|
+
updateScheduleInput(schedule);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Helper to set the value of the schedule input textbox when it has been updated
|
|
119
|
+
function updateScheduleInput(value) {
|
|
120
|
+
Ember.$('#schedule-input').find('input').val(value);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Called when updates made to the cron GUI, this will update the schedule record
|
|
124
|
+
function handleUpdate(self, $this) {
|
|
125
|
+
// Get the schedule
|
|
126
|
+
var schedule = self.get('schedule');
|
|
127
|
+
|
|
128
|
+
// The 'id' of the event which we use to defer the type
|
|
129
|
+
// it's either the href with the # stripped off if it's a
|
|
130
|
+
// tab click or the actual id attribute of the element
|
|
131
|
+
var id;
|
|
132
|
+
|
|
133
|
+
// If the href exists we can assume it's a tab
|
|
134
|
+
if (typeof $this.attr('href') !== 'undefined') {
|
|
135
|
+
id = $this.attr('href').substr(1);
|
|
136
|
+
} else {
|
|
137
|
+
id = $this.attr('id');
|
|
138
|
+
|
|
139
|
+
// If it's a checkbox set the id to it's parents parent
|
|
140
|
+
if ($this.attr('type') === 'checkbox') {
|
|
141
|
+
id = $this.closest('.tab-pane').attr('id');
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// If it's a every-n-value then strip off the -value
|
|
145
|
+
else if (id.substr(-6) === '-value') {
|
|
146
|
+
id = id.substr(0, id.length - 6);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Define the types of schedules we are searching for
|
|
151
|
+
var every_n = ['every-minute', 'every-hour', 'every-day-of-the-month', 'every-month', 'every-day-of-the-week'];
|
|
152
|
+
var every_n_type = ['every-n-minutes', 'every-n-hours'];
|
|
153
|
+
|
|
154
|
+
// Define the key/value vars used when the change to the schedule is set
|
|
155
|
+
var key, value = '';
|
|
156
|
+
|
|
157
|
+
// Is it an 'every n' i.e *
|
|
158
|
+
if (every_n.indexOf(id) >= 0) {
|
|
159
|
+
// Transform the id into a key for the schedule, replace - with _
|
|
160
|
+
key = id.substr(6).split('-').join('_');
|
|
161
|
+
value = '*';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Is it any 'every n type' i.e */n
|
|
165
|
+
else if (every_n_type.indexOf(id) >= 0) {
|
|
166
|
+
// Transform the id into a key for the schedule, replace - with _ and strip the trailing s
|
|
167
|
+
key = id.substr(8, (id.length - 1) - 8).split('-').join('_');
|
|
168
|
+
|
|
169
|
+
// Set the value using the value of the input
|
|
170
|
+
value = '*/' + Ember.$('#' + id + '-value').val();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Otherwise we can assume it's a each selected type
|
|
174
|
+
else {
|
|
175
|
+
// Transform the id into a key for the schedule, removing the every-selected-
|
|
176
|
+
// and replace- with _
|
|
177
|
+
key = id.substr(14).split('-').join('_');
|
|
178
|
+
|
|
179
|
+
// Loop every checkbox
|
|
180
|
+
Ember.$('#' + id).find('input[type="checkbox"]').each(function(k, v) {
|
|
181
|
+
// If the checkbox is checked add the value
|
|
182
|
+
if (v.checked === true) {
|
|
183
|
+
value += Ember.$(this).data('value') + ',';
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
// If no value has been set i.e no checkboxes are ticked, default to *
|
|
188
|
+
if (value.length === 0) {
|
|
189
|
+
value = '*';
|
|
190
|
+
// Otherwise we need to remove the trailing ,
|
|
191
|
+
} else {
|
|
192
|
+
if (value.substr(-1) === ',') {
|
|
193
|
+
value = value.substr(0, value.length - 1);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Update the schedule
|
|
199
|
+
console.log('setting', key, value);
|
|
200
|
+
schedule.set(key, value);
|
|
201
|
+
|
|
202
|
+
// Update the schedule input text box
|
|
203
|
+
updateScheduleInput(schedule.get('formatted'));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
Minicron.ScheduleEditorComponent = Ember.Component.extend({
|
|
207
|
+
didInsertElement: function() {
|
|
208
|
+
this.store = this.get('targetObject.store');
|
|
209
|
+
var self = this;
|
|
210
|
+
|
|
211
|
+
// Do we already have the schedule id? i.e we are editing a schedule
|
|
212
|
+
if (this.get('schedule_id')) {
|
|
213
|
+
// Look up the schedule
|
|
214
|
+
this.store.find('schedule', this.get('schedule_id')).then(function(schedule) {
|
|
215
|
+
self.set('schedule', schedule);
|
|
216
|
+
|
|
217
|
+
// Parse the schedule and display it in the GUI
|
|
218
|
+
parseSchedule(self.get('schedule.formatted'));
|
|
219
|
+
});
|
|
220
|
+
// If not we must be adding a new schedule
|
|
221
|
+
} else {
|
|
222
|
+
// Create a default schedule record
|
|
223
|
+
this.set('schedule', this.store.createRecord('schedule', {
|
|
224
|
+
minute: '*',
|
|
225
|
+
hour: '*',
|
|
226
|
+
day_of_the_month: '*',
|
|
227
|
+
month: '*',
|
|
228
|
+
day_of_the_week: '*'
|
|
229
|
+
}));
|
|
230
|
+
|
|
231
|
+
// Parse the schedule and display it in the GUI
|
|
232
|
+
parseSchedule(this.get('schedule.formatted'));
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Update our reference to 'this' so we can pass it to the update handlers
|
|
236
|
+
self = this;
|
|
237
|
+
|
|
238
|
+
// Handle when one of the tabs or labels (checkboxes) is clicked in readonly mode
|
|
239
|
+
Ember.$('#schedule-editor').find('a[data-toggle="tab"], .btn-group > label').on('click', function(e) {
|
|
240
|
+
var $this = Ember.$(this);
|
|
241
|
+
|
|
242
|
+
if (self.get('read_only')) {
|
|
243
|
+
e.preventDefault();
|
|
244
|
+
|
|
245
|
+
// Is it a label (checkbox)? Then we want to stop the click event bubbling
|
|
246
|
+
// up the DOM so the label doesn't get highlighted
|
|
247
|
+
if ($this[0].nodeName === 'LABEL') {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
// Only handle the update if it isn't a label (checkbox)
|
|
252
|
+
if ($this[0].nodeName !== 'LABEL') {
|
|
253
|
+
handleUpdate(self, $this);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
// Handle when one of the 'every n x' is inputs is changed
|
|
259
|
+
Ember.$('#schedule-editor').find('input[type="number"], input[type="checkbox"]').on('change', function(e) {
|
|
260
|
+
if (self.get('read_only')) {
|
|
261
|
+
e.preventDefault();
|
|
262
|
+
return false;
|
|
263
|
+
} else {
|
|
264
|
+
handleUpdate(self, Ember.$(this));
|
|
265
|
+
}
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
actions: {
|
|
269
|
+
save: function() {
|
|
270
|
+
this.sendAction('save', {
|
|
271
|
+
job_id: this.get('job_id'),
|
|
272
|
+
schedule: this.get('schedule')
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
cancel: function(schedule) {
|
|
276
|
+
this.sendAction('cancel', schedule);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
})();
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function() {
|
|
4
|
+
function deleteExecution(self, execution) {
|
|
5
|
+
var confirmation = "Are you sure want to delete this execution?";
|
|
6
|
+
|
|
7
|
+
if (window.confirm(confirmation)) {
|
|
8
|
+
// TODO: Do the same cascading delete that happens on the backend
|
|
9
|
+
// i.e deleting this execution also deletes the job execution output
|
|
10
|
+
execution.deleteRecord();
|
|
11
|
+
|
|
12
|
+
execution.save().then(function() {
|
|
13
|
+
self.transitionToRoute('executions');
|
|
14
|
+
}, function(response) {
|
|
15
|
+
execution.rollback();
|
|
16
|
+
console.log(response);
|
|
17
|
+
window.prompt('Error deleting execution, reason:', response.responseJSON.error);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
Minicron.ExecutionIndexController = Ember.ObjectController.extend({
|
|
23
|
+
actions: {
|
|
24
|
+
delete: function(execution) {
|
|
25
|
+
deleteExecution(this, execution);
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
sortedOutput: (function() {
|
|
29
|
+
return Ember.ArrayProxy.createWithMixins(Ember.SortableMixin, {
|
|
30
|
+
sortProperties: ['seq'],
|
|
31
|
+
content: this.get('content.job_execution_outputs')
|
|
32
|
+
});
|
|
33
|
+
}).property('content.job_execution_outputs')
|
|
34
|
+
});
|
|
35
|
+
})();
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function() {
|
|
4
|
+
function deleteHost(self, host) {
|
|
5
|
+
var confirmation = "Are you sure want to delete this host?\n\n";
|
|
6
|
+
confirmation += 'All associated data i.e jobs on this host and the jobs in this hosts crontab will be REMOVED!';
|
|
7
|
+
|
|
8
|
+
if (window.confirm(confirmation)) {
|
|
9
|
+
// TODO: Do the same cascading delete that happens on the backend
|
|
10
|
+
// i.e deleting jobs -> executions -> job_execution_outputs linked
|
|
11
|
+
// to this host
|
|
12
|
+
host.deleteRecord();
|
|
13
|
+
|
|
14
|
+
host.save().then(function() {
|
|
15
|
+
self.transitionToRoute('hosts');
|
|
16
|
+
}, function(response) {
|
|
17
|
+
host.rollback();
|
|
18
|
+
console.log(response);
|
|
19
|
+
window.prompt('Error deleting host, reason:', response.responseJSON.error);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function testConnection(self, host) {
|
|
25
|
+
self.set('test_connection', 'Testing..');
|
|
26
|
+
|
|
27
|
+
jQuery.getJSON('/api/hosts/' + host.id + '/test_ssh').done(function(data) {
|
|
28
|
+
// Could we at least connect to the host?
|
|
29
|
+
if (data.connect) {
|
|
30
|
+
// TODO: make this use a bootstrap model as a component
|
|
31
|
+
var results = 'Test Results\n\n';
|
|
32
|
+
results += 'connect: ' + data.connect + '\n';
|
|
33
|
+
results += 'crontab readable: ' + data.read + '\n';
|
|
34
|
+
results += 'crontab writeable: ' + data.write;
|
|
35
|
+
|
|
36
|
+
window.alert(results);
|
|
37
|
+
} else {
|
|
38
|
+
window.prompt('Failed to connect to host, reason:', data.responseJSON.error);
|
|
39
|
+
}
|
|
40
|
+
// If the actual ajax request failed
|
|
41
|
+
}).fail(function(xhr, status, error) {
|
|
42
|
+
console.log(xhr, status, error);
|
|
43
|
+
window.prompt('Failed to connect to API, reason:', xhr.responseJSON.error);
|
|
44
|
+
}).always(function() {
|
|
45
|
+
self.set('test_connection', 'Test Connection');
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Minicron.HostsIndexController = Ember.ObjectController.extend({
|
|
50
|
+
actions: {
|
|
51
|
+
delete: function(host) {
|
|
52
|
+
deleteHost(this, host);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
Minicron.HostIndexController = Ember.ObjectController.extend({
|
|
58
|
+
test_connection: 'Test Connection',
|
|
59
|
+
actions: {
|
|
60
|
+
delete: function(host) {
|
|
61
|
+
deleteHost(this, host);
|
|
62
|
+
},
|
|
63
|
+
test: function(host) {
|
|
64
|
+
testConnection(this, host);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
Minicron.HostsNewController = Ember.Controller.extend({
|
|
70
|
+
actions: {
|
|
71
|
+
save: function() {
|
|
72
|
+
var self = this,
|
|
73
|
+
host = this.store.createRecord('host', {
|
|
74
|
+
name: this.get('name'),
|
|
75
|
+
fqdn: this.get('fqdn'),
|
|
76
|
+
host: this.get('host'),
|
|
77
|
+
port: this.get('port'),
|
|
78
|
+
public_key: this.get('public_key')
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
host.save().then(function(host) {
|
|
82
|
+
self.transitionToRoute('host', host);
|
|
83
|
+
// TODO: better error handling here
|
|
84
|
+
}).catch(function(host) {
|
|
85
|
+
window.alert('Error saving host!');
|
|
86
|
+
console.log(host);
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
cancel: function(host) {
|
|
90
|
+
this.transitionToRoute('hosts');
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
Minicron.HostEditController = Ember.ObjectController.extend({
|
|
96
|
+
test_connection: 'Test Connection',
|
|
97
|
+
actions: {
|
|
98
|
+
save: function() {
|
|
99
|
+
var self = this,
|
|
100
|
+
host = this.store.push('host', {
|
|
101
|
+
id: this.get('id'),
|
|
102
|
+
name: this.get('name'),
|
|
103
|
+
fqdn: this.get('fqdn'),
|
|
104
|
+
host: this.get('host'),
|
|
105
|
+
port: this.get('port'),
|
|
106
|
+
public_key: this.get('public_key')
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
host.save().then(function(host) {
|
|
110
|
+
self.transitionToRoute('host', host);
|
|
111
|
+
// TODO: better error handling here
|
|
112
|
+
}).catch(function(host) {
|
|
113
|
+
window.alert('Error saving host!');
|
|
114
|
+
console.log(host);
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
delete: function(host) {
|
|
118
|
+
deleteHost(this, host);
|
|
119
|
+
},
|
|
120
|
+
test: function(host) {
|
|
121
|
+
testConnection(this, host);
|
|
122
|
+
},
|
|
123
|
+
cancel: function(host) {
|
|
124
|
+
host.rollback();
|
|
125
|
+
this.transitionToRoute('host', host);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
})();
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
(function() {
|
|
4
|
+
function deleteJob(self, job) {
|
|
5
|
+
var confirmation = "Are you sure want to delete this job?\n\n";
|
|
6
|
+
confirmation += 'All associated data will be deleted and it will be REMOVED from the host!';
|
|
7
|
+
|
|
8
|
+
if (window.confirm(confirmation)) {
|
|
9
|
+
// TODO: Do the same cascading delete that happens on the backend
|
|
10
|
+
// i.e deleting jobs -> executions -> job_execution_outputs linked
|
|
11
|
+
// to this job
|
|
12
|
+
job.deleteRecord();
|
|
13
|
+
|
|
14
|
+
job.save().then(function() {
|
|
15
|
+
self.transitionToRoute('jobs');
|
|
16
|
+
}, function(response) {
|
|
17
|
+
job.rollback();
|
|
18
|
+
console.log(response);
|
|
19
|
+
window.prompt('Error deleting job, reason:', response.responseJSON.error);
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
Minicron.JobsIndexController = Ember.ObjectController.extend({
|
|
25
|
+
actions: {
|
|
26
|
+
delete: function(job) {
|
|
27
|
+
deleteJob(this, job);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
Minicron.JobsNewController = Ember.ObjectController.extend({
|
|
33
|
+
save_button: 'Save',
|
|
34
|
+
job_name: null,
|
|
35
|
+
job_command: null,
|
|
36
|
+
actions: {
|
|
37
|
+
save: function() {
|
|
38
|
+
var self = this,
|
|
39
|
+
job = this.store.createRecord('job', {
|
|
40
|
+
name: this.get('job_name'),
|
|
41
|
+
command: this.get('job_command'),
|
|
42
|
+
});
|
|
43
|
+
// Let the user know the job is being saved
|
|
44
|
+
this.set('save_button', 'Saving..');
|
|
45
|
+
|
|
46
|
+
// Look up the host
|
|
47
|
+
this.store.find('host', this.get('id')).then(function(host) {
|
|
48
|
+
// Set the job relationship
|
|
49
|
+
job.set('host', host);
|
|
50
|
+
|
|
51
|
+
job.save().then(function(job) {
|
|
52
|
+
// Reset the save button text
|
|
53
|
+
self.set('save_button', 'Save');
|
|
54
|
+
|
|
55
|
+
self.transitionToRoute('job', job);
|
|
56
|
+
// TODO: better error handling here
|
|
57
|
+
}).catch(function(job) {
|
|
58
|
+
// Reset the save button text
|
|
59
|
+
self.set('save_button', 'Save');
|
|
60
|
+
|
|
61
|
+
window.alert('Error saving job!');
|
|
62
|
+
console.log(job);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
cancel: function() {
|
|
67
|
+
this.transitionToRoute('jobs');
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
Minicron.JobIndexController = Ember.ObjectController.extend({
|
|
73
|
+
actions: {
|
|
74
|
+
delete: function(job) {
|
|
75
|
+
deleteJob(this, job);
|
|
76
|
+
},
|
|
77
|
+
test: function(job) {
|
|
78
|
+
testConnection(this, job);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
Minicron.JobEditController = Ember.ObjectController.extend({
|
|
84
|
+
actions: {
|
|
85
|
+
save: function() {
|
|
86
|
+
var self = this,
|
|
87
|
+
job = this.store.push('job', {
|
|
88
|
+
id: this.get('id'),
|
|
89
|
+
name: this.get('name')
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
job.save().then(function(job) {
|
|
93
|
+
self.transitionToRoute('job', job);
|
|
94
|
+
// TODO: better error handling here
|
|
95
|
+
}).catch(function(job) {
|
|
96
|
+
window.alert('Error saving job!');
|
|
97
|
+
console.log(job);
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
delete: function(job) {
|
|
101
|
+
deleteJob(this, job);
|
|
102
|
+
},
|
|
103
|
+
cancel: function(job) {
|
|
104
|
+
job.rollback();
|
|
105
|
+
this.transitionToRoute('job', job);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
})();
|