minicron 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +187 -0
  4. data/Rakefile +17 -0
  5. data/bin/minicron +26 -0
  6. data/lib/minicron.rb +179 -0
  7. data/lib/minicron/alert.rb +115 -0
  8. data/lib/minicron/alert/email.rb +50 -0
  9. data/lib/minicron/alert/pagerduty.rb +39 -0
  10. data/lib/minicron/alert/sms.rb +47 -0
  11. data/lib/minicron/cli.rb +367 -0
  12. data/lib/minicron/constants.rb +7 -0
  13. data/lib/minicron/cron.rb +192 -0
  14. data/lib/minicron/hub/app.rb +132 -0
  15. data/lib/minicron/hub/assets/app/application.js +151 -0
  16. data/lib/minicron/hub/assets/app/components/schedules.js +280 -0
  17. data/lib/minicron/hub/assets/app/controllers/executions.js +35 -0
  18. data/lib/minicron/hub/assets/app/controllers/hosts.js +129 -0
  19. data/lib/minicron/hub/assets/app/controllers/jobs.js +109 -0
  20. data/lib/minicron/hub/assets/app/controllers/schedules.js +80 -0
  21. data/lib/minicron/hub/assets/app/helpers.js +22 -0
  22. data/lib/minicron/hub/assets/app/models/execution.js +13 -0
  23. data/lib/minicron/hub/assets/app/models/host.js +15 -0
  24. data/lib/minicron/hub/assets/app/models/job.js +15 -0
  25. data/lib/minicron/hub/assets/app/models/job_execution_output.js +11 -0
  26. data/lib/minicron/hub/assets/app/models/schedule.js +32 -0
  27. data/lib/minicron/hub/assets/app/router.js +31 -0
  28. data/lib/minicron/hub/assets/app/routes/executions.js +36 -0
  29. data/lib/minicron/hub/assets/app/routes/hosts.js +42 -0
  30. data/lib/minicron/hub/assets/app/routes/index.js +9 -0
  31. data/lib/minicron/hub/assets/app/routes/jobs.js +52 -0
  32. data/lib/minicron/hub/assets/app/routes/schedules.js +37 -0
  33. data/lib/minicron/hub/assets/css/bootswatch.min.css +9 -0
  34. data/lib/minicron/hub/assets/css/main.scss +323 -0
  35. data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.eot +0 -0
  36. data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.svg +229 -0
  37. data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.ttf +0 -0
  38. data/lib/minicron/hub/assets/fonts/glyphicons-halflings-regular.woff +0 -0
  39. data/lib/minicron/hub/assets/fonts/lato-bold-700.woff +0 -0
  40. data/lib/minicron/hub/assets/fonts/lato-italic-400.woff +0 -0
  41. data/lib/minicron/hub/assets/fonts/lato-regular-400.woff +0 -0
  42. data/lib/minicron/hub/assets/js/ansi_up-1.1.1.min.js +6 -0
  43. data/lib/minicron/hub/assets/js/auth/ember-auth-9.0.7.min.js +2 -0
  44. data/lib/minicron/hub/assets/js/auth/ember-auth-request-jquery-1.0.3.min.js +1 -0
  45. data/lib/minicron/hub/assets/js/bootstrap-3.1.1.min.js +6 -0
  46. data/lib/minicron/hub/assets/js/ember-1.4.1.min.js +18 -0
  47. data/lib/minicron/hub/assets/js/ember-data-1.0.0-beta.7.f87cba88.min.js +10 -0
  48. data/lib/minicron/hub/assets/js/faye-browser-1.0.1.min.js +2 -0
  49. data/lib/minicron/hub/assets/js/handlebars-1.3.0.min.js +29 -0
  50. data/lib/minicron/hub/assets/js/jquery-2.1.0.min.js +4 -0
  51. data/lib/minicron/hub/assets/js/moment-2.5.1.min.js +7 -0
  52. data/lib/minicron/hub/controllers/api/executions.rb +34 -0
  53. data/lib/minicron/hub/controllers/api/hosts.rb +150 -0
  54. data/lib/minicron/hub/controllers/api/job_execution_outputs.rb +30 -0
  55. data/lib/minicron/hub/controllers/api/jobs.rb +118 -0
  56. data/lib/minicron/hub/controllers/api/schedule.rb +184 -0
  57. data/lib/minicron/hub/controllers/index.rb +5 -0
  58. data/lib/minicron/hub/db/schema.rb +98 -0
  59. data/lib/minicron/hub/db/schema.sql +158 -0
  60. data/lib/minicron/hub/models/alert.rb +7 -0
  61. data/lib/minicron/hub/models/execution.rb +8 -0
  62. data/lib/minicron/hub/models/host.rb +7 -0
  63. data/lib/minicron/hub/models/job.rb +18 -0
  64. data/lib/minicron/hub/models/job_execution_output.rb +7 -0
  65. data/lib/minicron/hub/models/schedule.rb +25 -0
  66. data/lib/minicron/hub/serializers/execution.rb +75 -0
  67. data/lib/minicron/hub/serializers/host.rb +57 -0
  68. data/lib/minicron/hub/serializers/job.rb +104 -0
  69. data/lib/minicron/hub/serializers/job_execution_output.rb +48 -0
  70. data/lib/minicron/hub/serializers/schedule.rb +68 -0
  71. data/lib/minicron/hub/views/handlebars/application.erb +51 -0
  72. data/lib/minicron/hub/views/handlebars/errors.erb +29 -0
  73. data/lib/minicron/hub/views/handlebars/executions.erb +79 -0
  74. data/lib/minicron/hub/views/handlebars/hosts.erb +205 -0
  75. data/lib/minicron/hub/views/handlebars/jobs.erb +203 -0
  76. data/lib/minicron/hub/views/handlebars/loading.erb +3 -0
  77. data/lib/minicron/hub/views/handlebars/schedules.erb +354 -0
  78. data/lib/minicron/hub/views/index.erb +7 -0
  79. data/lib/minicron/hub/views/layouts/app.erb +15 -0
  80. data/lib/minicron/monitor.rb +116 -0
  81. data/lib/minicron/transport.rb +15 -0
  82. data/lib/minicron/transport/client.rb +80 -0
  83. data/lib/minicron/transport/faye/client.rb +103 -0
  84. data/lib/minicron/transport/faye/extensions/job_handler.rb +184 -0
  85. data/lib/minicron/transport/faye/server.rb +58 -0
  86. data/lib/minicron/transport/server.rb +62 -0
  87. data/lib/minicron/transport/ssh.rb +51 -0
  88. data/spec/invalid_config.toml +2 -0
  89. data/spec/minicron/cli_spec.rb +154 -0
  90. data/spec/minicron/transport/client_spec.rb +8 -0
  91. data/spec/minicron/transport/faye/client_spec.rb +53 -0
  92. data/spec/minicron/transport/server_spec.rb +70 -0
  93. data/spec/minicron/transport_spec.rb +13 -0
  94. data/spec/minicron_spec.rb +133 -0
  95. data/spec/spec_helper.rb +33 -0
  96. data/spec/valid_config.toml +48 -0
  97. metadata +577 -0
@@ -0,0 +1,3 @@
1
+ <script type="text/x-handlebars" id="loading">
2
+ <img src="data:image/gif;base64,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"/>
3
+ </script>
@@ -0,0 +1,354 @@
1
+ <script type="text/x-handlebars" id="components/schedule-editor">
2
+ <form class="form-horizontal" role="form" {{action 'save' on='submit'}}>
3
+ <div class="panel-group" id="schedule-editor">
4
+ <div class="panel panel-default">
5
+ <a data-toggle="collapse" data-parent="#schedule-editor" href="#schedule-minute">
6
+ <div class="panel-heading">
7
+ <h4 class="panel-title">Minute</h4>
8
+ </div>
9
+ </a>
10
+ <div id="schedule-minute" class="panel-collapse collapse in">
11
+ <div class="panel-body">
12
+ <ul class="nav nav-tabs">
13
+ <li class="active"><a href="#every-minute" data-toggle="tab">every minute</a></li>
14
+ <li><a href="#every-n-minutes" data-toggle="tab">every n minutes</a></li>
15
+ <li><a href="#each-selected-minute" data-toggle="tab">each selected minute</a></li>
16
+ </ul>
17
+
18
+ <div class="tab-content">
19
+ <div class="tab-pane active" id="every-minute">The job will run every minute</div>
20
+ <div class="tab-pane" id="every-n-minutes">
21
+ The job will run every <input id="every-n-minutes-value" type="number" min="1" max="59" value="5" class="every-n" {{bind-attr disabled=read_only}}></span> minutes
22
+ </div>
23
+ <div class="tab-pane" id="each-selected-minute">
24
+ <div class="btn-group clearfix" data-toggle="buttons">
25
+ <label class="btn btn-primary"><input type="checkbox" data-value="0"/>00</label>
26
+ <label class="btn btn-primary"><input type="checkbox" data-value="1"/>01</label>
27
+ <label class="btn btn-primary"><input type="checkbox" data-value="2"/>02</label>
28
+ <label class="btn btn-primary"><input type="checkbox" data-value="3"/>03</label>
29
+ <label class="btn btn-primary"><input type="checkbox" data-value="4"/>04</label>
30
+ <label class="btn btn-primary"><input type="checkbox" data-value="5"/>05</label>
31
+ <label class="btn btn-primary"><input type="checkbox" data-value="6"/>06</label>
32
+ <label class="btn btn-primary"><input type="checkbox" data-value="7"/>07</label>
33
+ <label class="btn btn-primary"><input type="checkbox" data-value="8"/>08</label>
34
+ <label class="btn btn-primary"><input type="checkbox" data-value="9"/>09</label>
35
+ </div>
36
+
37
+ <div class="btn-group clearfix" data-toggle="buttons">
38
+ <label class="btn btn-primary"><input type="checkbox" data-value="10"/>10</label>
39
+ <label class="btn btn-primary"><input type="checkbox" data-value="11"/>11</label>
40
+ <label class="btn btn-primary"><input type="checkbox" data-value="12"/>12</label>
41
+ <label class="btn btn-primary"><input type="checkbox" data-value="13"/>13</label>
42
+ <label class="btn btn-primary"><input type="checkbox" data-value="14"/>14</label>
43
+ <label class="btn btn-primary"><input type="checkbox" data-value="15"/>15</label>
44
+ <label class="btn btn-primary"><input type="checkbox" data-value="16"/>16</label>
45
+ <label class="btn btn-primary"><input type="checkbox" data-value="17"/>17</label>
46
+ <label class="btn btn-primary"><input type="checkbox" data-value="18"/>18</label>
47
+ <label class="btn btn-primary"><input type="checkbox" data-value="19"/>19</label>
48
+ </div>
49
+
50
+ <div class="btn-group clearfix" data-toggle="buttons">
51
+ <label class="btn btn-primary"><input type="checkbox" data-value="20"/>20</label>
52
+ <label class="btn btn-primary"><input type="checkbox" data-value="21"/>21</label>
53
+ <label class="btn btn-primary"><input type="checkbox" data-value="22"/>22</label>
54
+ <label class="btn btn-primary"><input type="checkbox" data-value="23"/>23</label>
55
+ <label class="btn btn-primary"><input type="checkbox" data-value="24"/>24</label>
56
+ <label class="btn btn-primary"><input type="checkbox" data-value="25"/>25</label>
57
+ <label class="btn btn-primary"><input type="checkbox" data-value="26"/>26</label>
58
+ <label class="btn btn-primary"><input type="checkbox" data-value="27"/>27</label>
59
+ <label class="btn btn-primary"><input type="checkbox" data-value="28"/>28</label>
60
+ <label class="btn btn-primary"><input type="checkbox" data-value="29"/>29</label>
61
+ </div>
62
+
63
+ <div class="btn-group clearfix" data-toggle="buttons">
64
+ <label class="btn btn-primary"><input type="checkbox" data-value="30"/>30</label>
65
+ <label class="btn btn-primary"><input type="checkbox" data-value="31"/>31</label>
66
+ <label class="btn btn-primary"><input type="checkbox" data-value="32"/>32</label>
67
+ <label class="btn btn-primary"><input type="checkbox" data-value="33"/>33</label>
68
+ <label class="btn btn-primary"><input type="checkbox" data-value="34"/>34</label>
69
+ <label class="btn btn-primary"><input type="checkbox" data-value="35"/>35</label>
70
+ <label class="btn btn-primary"><input type="checkbox" data-value="36"/>36</label>
71
+ <label class="btn btn-primary"><input type="checkbox" data-value="37"/>37</label>
72
+ <label class="btn btn-primary"><input type="checkbox" data-value="38"/>38</label>
73
+ <label class="btn btn-primary"><input type="checkbox" data-value="39"/>39</label>
74
+ </div>
75
+
76
+ <div class="btn-group clearfix" data-toggle="buttons">
77
+ <label class="btn btn-primary"><input type="checkbox" data-value="40"/>40</label>
78
+ <label class="btn btn-primary"><input type="checkbox" data-value="41"/>41</label>
79
+ <label class="btn btn-primary"><input type="checkbox" data-value="42"/>42</label>
80
+ <label class="btn btn-primary"><input type="checkbox" data-value="43"/>43</label>
81
+ <label class="btn btn-primary"><input type="checkbox" data-value="44"/>44</label>
82
+ <label class="btn btn-primary"><input type="checkbox" data-value="45"/>45</label>
83
+ <label class="btn btn-primary"><input type="checkbox" data-value="46"/>46</label>
84
+ <label class="btn btn-primary"><input type="checkbox" data-value="47"/>47</label>
85
+ <label class="btn btn-primary"><input type="checkbox" data-value="48"/>48</label>
86
+ <label class="btn btn-primary"><input type="checkbox" data-value="49"/>49</label>
87
+ </div>
88
+
89
+ <div class="btn-group clearfix" data-toggle="buttons">
90
+ <label class="btn btn-primary"><input type="checkbox" data-value="50"/>50</label>
91
+ <label class="btn btn-primary"><input type="checkbox" data-value="51"/>51</label>
92
+ <label class="btn btn-primary"><input type="checkbox" data-value="52"/>52</label>
93
+ <label class="btn btn-primary"><input type="checkbox" data-value="53"/>53</label>
94
+ <label class="btn btn-primary"><input type="checkbox" data-value="54"/>54</label>
95
+ <label class="btn btn-primary"><input type="checkbox" data-value="55"/>55</label>
96
+ <label class="btn btn-primary"><input type="checkbox" data-value="56"/>56</label>
97
+ <label class="btn btn-primary"><input type="checkbox" data-value="57"/>57</label>
98
+ <label class="btn btn-primary"><input type="checkbox" data-value="58"/>58</label>
99
+ <label class="btn btn-primary"><input type="checkbox" data-value="59"/>59</label>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ <div class="panel panel-default">
107
+ <a data-toggle="collapse" data-parent="#schedule-editor" href="#schedule-hour">
108
+ <div class="panel-heading">
109
+ <h4 class="panel-title">Hour</h4>
110
+ </div>
111
+ </a>
112
+ <div id="schedule-hour" class="panel-collapse collapse">
113
+ <div class="panel-body">
114
+ <ul class="nav nav-tabs">
115
+ <li class="active"><a href="#every-hour" data-toggle="tab">every hour</a></li>
116
+ <li><a href="#every-n-hours" data-toggle="tab">every n hours</a></li>
117
+ <li><a href="#each-selected-hour" data-toggle="tab">each selected hour</a></li>
118
+ </ul>
119
+
120
+ <div class="tab-content">
121
+ <div class="tab-pane active" id="every-hour">The job will run every hour</div>
122
+ <div class="tab-pane" id="every-n-hours">
123
+ The job will run every <input id="every-n-hours-value" type="number" min="1" max="23" value="5" class="every-n" {{bind-attr disabled=read_only}}></span> hours
124
+ </div>
125
+ <div class="tab-pane" id="each-selected-hour">
126
+ <div class="btn-group clearfix" data-toggle="buttons">
127
+ <label class="btn btn-primary"><input type="checkbox" data-value="0"/>00</label>
128
+ <label class="btn btn-primary"><input type="checkbox" data-value="1"/>01</label>
129
+ <label class="btn btn-primary"><input type="checkbox" data-value="2"/>02</label>
130
+ <label class="btn btn-primary"><input type="checkbox" data-value="3"/>03</label>
131
+ <label class="btn btn-primary"><input type="checkbox" data-value="4"/>04</label>
132
+ <label class="btn btn-primary"><input type="checkbox" data-value="5"/>05</label>
133
+ <label class="btn btn-primary"><input type="checkbox" data-value="6"/>06</label>
134
+ <label class="btn btn-primary"><input type="checkbox" data-value="7"/>07</label>
135
+ <label class="btn btn-primary"><input type="checkbox" data-value="8"/>08</label>
136
+ <label class="btn btn-primary"><input type="checkbox" data-value="9"/>09</label>
137
+ <label class="btn btn-primary"><input type="checkbox" data-value="10"/>10</label>
138
+ <label class="btn btn-primary"><input type="checkbox" data-value="11"/>11</label>
139
+ </div>
140
+
141
+ <div class="btn-group clearfix" data-toggle="buttons">
142
+ <label class="btn btn-primary"><input type="checkbox" data-value="12"/>12</label>
143
+ <label class="btn btn-primary"><input type="checkbox" data-value="13"/>13</label>
144
+ <label class="btn btn-primary"><input type="checkbox" data-value="14"/>14</label>
145
+ <label class="btn btn-primary"><input type="checkbox" data-value="15"/>15</label>
146
+ <label class="btn btn-primary"><input type="checkbox" data-value="16"/>16</label>
147
+ <label class="btn btn-primary"><input type="checkbox" data-value="17"/>17</label>
148
+ <label class="btn btn-primary"><input type="checkbox" data-value="18"/>18</label>
149
+ <label class="btn btn-primary"><input type="checkbox" data-value="19"/>19</label>
150
+ <label class="btn btn-primary"><input type="checkbox" data-value="20"/>20</label>
151
+ <label class="btn btn-primary"><input type="checkbox" data-value="21"/>21</label>
152
+ <label class="btn btn-primary"><input type="checkbox" data-value="22"/>22</label>
153
+ <label class="btn btn-primary"><input type="checkbox" data-value="23"/>23</label>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+ <div class="panel panel-default">
161
+ <a data-toggle="collapse" data-parent="#schedule-editor" href="#schedule-dotm">
162
+ <div class="panel-heading">
163
+ <h4 class="panel-title">Day of the month</h4>
164
+ </div>
165
+ </a>
166
+ <div id="schedule-dotm" class="panel-collapse collapse">
167
+ <div class="panel-body">
168
+ <ul class="nav nav-tabs">
169
+ <li class="active"><a href="#every-day-of-the-month" data-toggle="tab">every day of the month</a></li>
170
+ <li><a href="#each-selected-day-of-the-month" data-toggle="tab">each selected day of the month</a></li>
171
+ </ul>
172
+
173
+ <div class="tab-content">
174
+ <div class="tab-pane active" id="every-day-of-the-month">The job will run every day of the month</div>
175
+ <div class="tab-pane" id="each-selected-day-of-the-month">
176
+ <div class="btn-group clearfix" data-toggle="buttons">
177
+ <label class="btn btn-primary"><input type="checkbox" data-value="1"/>01</label>
178
+ <label class="btn btn-primary"><input type="checkbox" data-value="2"/>02</label>
179
+ <label class="btn btn-primary"><input type="checkbox" data-value="3"/>03</label>
180
+ <label class="btn btn-primary"><input type="checkbox" data-value="4"/>04</label>
181
+ <label class="btn btn-primary"><input type="checkbox" data-value="5"/>05</label>
182
+ <label class="btn btn-primary"><input type="checkbox" data-value="6"/>06</label>
183
+ <label class="btn btn-primary"><input type="checkbox" data-value="7"/>07</label>
184
+ </div>
185
+
186
+ <div class="btn-group clearfix" data-toggle="buttons">
187
+ <label class="btn btn-primary"><input type="checkbox" data-value="8"/>08</label>
188
+ <label class="btn btn-primary"><input type="checkbox" data-value="9"/>09</label>
189
+ <label class="btn btn-primary"><input type="checkbox" data-value="10"/>10</label>
190
+ <label class="btn btn-primary"><input type="checkbox" data-value="11"/>11</label>
191
+ <label class="btn btn-primary"><input type="checkbox" data-value="12"/>12</label>
192
+ <label class="btn btn-primary"><input type="checkbox" data-value="13"/>13</label>
193
+ <label class="btn btn-primary"><input type="checkbox" data-value="14"/>14</label>
194
+ </div>
195
+
196
+ <div class="btn-group clearfix" data-toggle="buttons">
197
+ <label class="btn btn-primary"><input type="checkbox" data-value="15"/>15</label>
198
+ <label class="btn btn-primary"><input type="checkbox" data-value="16"/>16</label>
199
+ <label class="btn btn-primary"><input type="checkbox" data-value="17"/>17</label>
200
+ <label class="btn btn-primary"><input type="checkbox" data-value="18"/>18</label>
201
+ <label class="btn btn-primary"><input type="checkbox" data-value="19"/>19</label>
202
+ <label class="btn btn-primary"><input type="checkbox" data-value="20"/>20</label>
203
+ <label class="btn btn-primary"><input type="checkbox" data-value="21"/>21</label>
204
+ </div>
205
+
206
+ <div class="btn-group clearfix" data-toggle="buttons">
207
+ <label class="btn btn-primary"><input type="checkbox" data-value="22"/>22</label>
208
+ <label class="btn btn-primary"><input type="checkbox" data-value="23"/>23</label>
209
+ <label class="btn btn-primary"><input type="checkbox" data-value="24"/>24</label>
210
+ <label class="btn btn-primary"><input type="checkbox" data-value="25"/>25</label>
211
+ <label class="btn btn-primary"><input type="checkbox" data-value="26"/>26</label>
212
+ <label class="btn btn-primary"><input type="checkbox" data-value="27"/>27</label>
213
+ <label class="btn btn-primary"><input type="checkbox" data-value="28"/>28</label>
214
+ </div>
215
+
216
+ <div class="btn-group clearfix" data-toggle="buttons">
217
+ <label class="btn btn-primary"><input type="checkbox" data-value="29"/>29</label>
218
+ <label class="btn btn-primary"><input type="checkbox" data-value="30"/>30</label>
219
+ <label class="btn btn-primary"><input type="checkbox" data-value="31"/>31</label>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ <div class="panel panel-default">
227
+ <a data-toggle="collapse" data-parent="#schedule-editor" href="#schedule-month">
228
+ <div class="panel-heading">
229
+ <h4 class="panel-title">Month</h4>
230
+ </div>
231
+ </a>
232
+ <div id="schedule-month" class="panel-collapse collapse">
233
+ <div class="panel-body">
234
+ <ul class="nav nav-tabs">
235
+ <li class="active"><a href="#every-month" data-toggle="tab">every month</a></li>
236
+ <li><a href="#each-selected-month" data-toggle="tab">each selected month</a></li>
237
+ </ul>
238
+
239
+ <div class="tab-content">
240
+ <div class="tab-pane active" id="every-month">The job will run every month</div>
241
+ <div class="tab-pane" id="each-selected-month">
242
+ <div class="btn-group clearfix" data-toggle="buttons">
243
+ <label class="btn btn-primary"><input type="checkbox" data-value="1"/>Jan</label>
244
+ <label class="btn btn-primary"><input type="checkbox" data-value="2"/>Feb</label>
245
+ <label class="btn btn-primary"><input type="checkbox" data-value="3"/>Mar</label>
246
+ <label class="btn btn-primary"><input type="checkbox" data-value="4"/>Apr</label>
247
+ <label class="btn btn-primary"><input type="checkbox" data-value="5"/>May</label>
248
+ <label class="btn btn-primary"><input type="checkbox" data-value="6"/>Jun</label>
249
+ <label class="btn btn-primary"><input type="checkbox" data-value="7"/>Jul</label>
250
+ <label class="btn btn-primary"><input type="checkbox" data-value="8"/>Aug</label>
251
+ <label class="btn btn-primary"><input type="checkbox" data-value="9"/>Sep</label>
252
+ <label class="btn btn-primary"><input type="checkbox" data-value="10"/>Oct</label>
253
+ <label class="btn btn-primary"><input type="checkbox" data-value="11"/>Nov</label>
254
+ <label class="btn btn-primary"><input type="checkbox" data-value="12"/>Dec</label>
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ </div>
260
+ </div>
261
+ <div class="panel panel-default">
262
+ <a data-toggle="collapse" data-parent="#schedule-editor" href="#schedule-dotw">
263
+ <div class="panel-heading">
264
+ <h4 class="panel-title">Day of week</h4>
265
+ </div>
266
+ </a>
267
+ <div id="schedule-dotw" class="panel-collapse collapse">
268
+ <div class="panel-body">
269
+ <ul class="nav nav-tabs">
270
+ <li class="active"><a href="#every-day-of-the-week" data-toggle="tab">every day of the week</a></li>
271
+ <li><a href="#each-selected-day-of-the-week" data-toggle="tab">each selected day of the week</a></li>
272
+ </ul>
273
+
274
+ <div class="tab-content">
275
+ <div class="tab-pane active" id="every-day-of-the-week">The job will run every day of the week</div>
276
+ <div class="tab-pane" id="each-selected-day-of-the-week">
277
+ <div class="btn-group clearfix" data-toggle="buttons">
278
+ <label class="btn btn-primary"><input type="checkbox" data-value="0"/>Sun</label>
279
+ <label class="btn btn-primary"><input type="checkbox" data-value="1"/>Mon</label>
280
+ <label class="btn btn-primary"><input type="checkbox" data-value="2"/>Tue</label>
281
+ <label class="btn btn-primary"><input type="checkbox" data-value="3"/>Wed</label>
282
+ <label class="btn btn-primary"><input type="checkbox" data-value="4"/>Thu</label>
283
+ <label class="btn btn-primary"><input type="checkbox" data-value="5"/>Fri</label>
284
+ <label class="btn btn-primary"><input type="checkbox" data-value="6"/>Sat</label>
285
+ </div>
286
+ </div>
287
+ </div>
288
+ </div>
289
+ </div>
290
+ </div>
291
+ </div>
292
+
293
+ <div class="form-group">
294
+ <hr/>
295
+ <label class="col-sm-2 control-label">Schedule</label>
296
+ <div class="col-sm-10">
297
+ {{input value=job_id type="hidden"}}
298
+ {{input value=schedule_id type="hidden"}}
299
+ <span id="schedule-input">{{input value=formatted disabled="disabled" type="text" class="form-control"}}</span>
300
+ </div>
301
+ </div>
302
+
303
+ {{#unless read_only}}
304
+ <div class="form-group">
305
+ <div class="col-sm-offset-2 col-sm-10">
306
+ <button type="submit" class="btn btn-success">Save</button>
307
+ <button {{action 'cancel' schedule}} class="btn btn-danger">Cancel</button>
308
+ </div>
309
+ </div>
310
+ {{/unless}}
311
+ </form>
312
+ </script>
313
+
314
+ <script type="text/x-handlebars" id="schedule/index">
315
+ <header class="clearfix">
316
+ <h2 class="pull-left">Schedule #{{id}} for Job '{{job.name}}'</h2>
317
+ <a {{action 'delete' this}} class="pull-right btn btn-danger btn-sm">Delete</a>
318
+ {{#link-to 'schedule.edit' id class="pull-right btn btn-warning btn-sm"}}Edit{{/link-to}}
319
+ </header>
320
+ <hr/>
321
+
322
+ {{schedule-editor
323
+ save='save'
324
+ cancel='cancel'
325
+ job_id=job.id
326
+ schedule_id=id
327
+ read_only=true}}
328
+ </script>
329
+
330
+ <script type="text/x-handlebars" id="schedules/new">
331
+ <header>
332
+ <h2>Add New Schedule for Job '{{name}}'</h2>
333
+ </header>
334
+ <hr/>
335
+
336
+ {{schedule-editor
337
+ save='save'
338
+ cancel='cancel'
339
+ job_id=id}}
340
+ </script>
341
+
342
+ <script type="text/x-handlebars" id="schedule/edit">
343
+ <header class="clearfix">
344
+ <h2 class="pull-left">Edit Schedule #{{id}} for Job '{{job.name}}'</h2>
345
+ <a {{action 'delete' this}} class="pull-right btn btn-danger btn-sm">Delete</a>
346
+ </header>
347
+ <hr/>
348
+
349
+ {{schedule-editor
350
+ save='save'
351
+ cancel='cancel'
352
+ job_id=job.id
353
+ schedule_id=id}}
354
+ </script>
@@ -0,0 +1,7 @@
1
+ <%= erb :'handlebars/loading' %>
2
+ <%= erb :'handlebars/errors' %>
3
+ <%= erb :'handlebars/application' %>
4
+ <%= erb :'handlebars/executions' %>
5
+ <%= erb :'handlebars/jobs' %>
6
+ <%= erb :'handlebars/schedules' %>
7
+ <%= erb :'handlebars/hosts' %>
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <title>minicron</title>
5
+
6
+ <%= css :app, :media => 'screen' %>
7
+ </head>
8
+ <body>
9
+
10
+ <%= yield %>
11
+
12
+ <%= js :app, :media => 'screen' %>
13
+
14
+ </body>
15
+ </html>
@@ -0,0 +1,116 @@
1
+ require 'active_record'
2
+ require 'parse-cron'
3
+ require 'minicron/hub/models/schedule'
4
+ require 'minicron/hub/models/execution'
5
+ require 'minicron/alert'
6
+
7
+ module Minicron
8
+ # Used to monitor the executions in the database and look for any failures
9
+ # or missed executions based on the schedules minicron knows about
10
+ class Monitor
11
+ def initialize
12
+ @active = false
13
+ end
14
+
15
+ # Establishes a database connection
16
+ def setup_db
17
+ case Minicron.config['database']['type']
18
+ when 'mysql'
19
+ # Establish a database connection
20
+ ActiveRecord::Base.establish_connection({
21
+ :adapter => 'mysql2',
22
+ :host => Minicron.config['database']['host'],
23
+ :database => Minicron.config['database']['database'],
24
+ :username => Minicron.config['database']['username'],
25
+ :password => Minicron.config['database']['password']
26
+ })
27
+ else
28
+ raise Exception, "The database #{Minicron.config['database']['type']} is not supported"
29
+ end
30
+ end
31
+
32
+ # Starts the execution monitor in a new thread
33
+ def start!
34
+ # Activate the monitor
35
+ @active = true
36
+
37
+ # Establish a database connection
38
+ setup_db
39
+
40
+ # Start a thread for the monitor
41
+ @thread = Thread.new do
42
+ # Delay the start of the monitor loop for a moniter so we don't immediately
43
+ # send an alert for a job the system wasn't 'up' for
44
+ sleep 60
45
+
46
+ # While the monitor is active run it in a loop ~every minute
47
+ while @active do
48
+ # Get all the schedules
49
+ schedules = Minicron::Hub::Schedule.all
50
+
51
+ # Loop every schedule we know about
52
+ schedules.each do |schedule|
53
+ begin
54
+ monitor(schedule)
55
+ rescue Exception => e
56
+ if Minicron.config['global']['trace']
57
+ puts e.message
58
+ puts e.backtrace
59
+ end
60
+ end
61
+ end
62
+
63
+ sleep 60
64
+ end
65
+ end
66
+ end
67
+
68
+ # Stops the execution monitor
69
+ def stop!
70
+ @active = false
71
+ @thread.join
72
+ end
73
+
74
+ # Is the execution monitor running?
75
+ def running?
76
+ @active
77
+ end
78
+
79
+ private
80
+
81
+ # Handle the monitoring of a cron schedule
82
+ #
83
+ # @param schedule [Minicron::Hub::Schedule]
84
+ def monitor(schedule)
85
+ # Get an instance of the alert class
86
+ alert = Minicron::Alert.new
87
+
88
+ # Parse the cron expression
89
+ cron = CronParser.new(schedule.formatted)
90
+
91
+ # Find the time the cron was last expected to run
92
+ expected_at = cron.last(Time.now.utc)
93
+
94
+ # We need to wait until after a minute past the expected run time
95
+ if Time.now.utc > (expected_at + 60)
96
+ # Check if this execution was created inside a minute window
97
+ # starting when it was expected to run
98
+ check = Minicron::Hub::Execution.exists?(
99
+ :created_at => expected_at..(expected_at + 60),
100
+ :job_id => schedule.job_id
101
+ )
102
+
103
+ # If the check failed
104
+ unless check
105
+ alert.send_all(
106
+ :kind => 'miss',
107
+ :schedule_id => schedule.id,
108
+ :expected_at => expected_at,
109
+ :job_id => schedule.job_id,
110
+ :expected_at => expected_at
111
+ )
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end