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.
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