kuroko2 0.2.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 (338) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +15 -0
  4. data/Rakefile +26 -0
  5. data/app/assets/images/kuroko2/avatar.png +0 -0
  6. data/app/assets/images/kuroko2/kuroko-logo-horizontal.png +0 -0
  7. data/app/assets/javascripts/kuroko2/application.js +28 -0
  8. data/app/assets/javascripts/kuroko2/bootstrap.js +2363 -0
  9. data/app/assets/javascripts/kuroko2/definition_linker.js +20 -0
  10. data/app/assets/javascripts/kuroko2/instance_linker.js +10 -0
  11. data/app/assets/javascripts/kuroko2/job_definition_stats.js +67 -0
  12. data/app/assets/javascripts/kuroko2/job_definitions.js +79 -0
  13. data/app/assets/javascripts/kuroko2/job_instances.js +88 -0
  14. data/app/assets/javascripts/kuroko2/job_timelines.js.coffee +24 -0
  15. data/app/assets/javascripts/kuroko2/narrow_job_definitions.js +30 -0
  16. data/app/assets/stylesheets/kuroko2/admin-lte.css +3402 -0
  17. data/app/assets/stylesheets/kuroko2/application.scss +86 -0
  18. data/app/assets/stylesheets/kuroko2/bootstrap.css +6799 -0
  19. data/app/assets/stylesheets/kuroko2/fonts.scss +1 -0
  20. data/app/assets/stylesheets/kuroko2/job_definitions.scss +19 -0
  21. data/app/assets/stylesheets/kuroko2/users.scss +7 -0
  22. data/app/controllers/kuroko2/api/application_controller.rb +49 -0
  23. data/app/controllers/kuroko2/api/job_instances_controller.rb +46 -0
  24. data/app/controllers/kuroko2/api/stats_controller.rb +28 -0
  25. data/app/controllers/kuroko2/application_controller.rb +43 -0
  26. data/app/controllers/kuroko2/dashboard_controller.rb +27 -0
  27. data/app/controllers/kuroko2/execution_logs_controller.rb +19 -0
  28. data/app/controllers/kuroko2/executions_controller.rb +28 -0
  29. data/app/controllers/kuroko2/job_definition_stats_controller.rb +54 -0
  30. data/app/controllers/kuroko2/job_definitions_controller.rb +100 -0
  31. data/app/controllers/kuroko2/job_instances_controller.rb +87 -0
  32. data/app/controllers/kuroko2/job_schedules_controller.rb +39 -0
  33. data/app/controllers/kuroko2/job_suspend_schedules_controller.rb +38 -0
  34. data/app/controllers/kuroko2/job_timelines_controller.rb +56 -0
  35. data/app/controllers/kuroko2/logs_controller.rb +15 -0
  36. data/app/controllers/kuroko2/sessions_controller.rb +32 -0
  37. data/app/controllers/kuroko2/stars_controller.rb +30 -0
  38. data/app/controllers/kuroko2/tokens_controller.rb +47 -0
  39. data/app/controllers/kuroko2/users_controller.rb +62 -0
  40. data/app/controllers/kuroko2/workers_controller.rb +5 -0
  41. data/app/errors/http/bad_request.rb +4 -0
  42. data/app/errors/http/forbidden.rb +4 -0
  43. data/app/errors/http/unauthorized.rb +4 -0
  44. data/app/helpers/kuroko2/application_helper.rb +8 -0
  45. data/app/helpers/kuroko2/dashboard_helper.rb +4 -0
  46. data/app/helpers/kuroko2/executions_helper.rb +4 -0
  47. data/app/helpers/kuroko2/job_definitions_helper.rb +38 -0
  48. data/app/helpers/kuroko2/job_instances_helper.rb +71 -0
  49. data/app/helpers/kuroko2/job_schedules_helper.rb +4 -0
  50. data/app/helpers/kuroko2/logs_helper.rb +4 -0
  51. data/app/helpers/kuroko2/sessions_helper.rb +4 -0
  52. data/app/helpers/kuroko2/stars_helper.rb +4 -0
  53. data/app/helpers/kuroko2/tokens_helper.rb +4 -0
  54. data/app/helpers/kuroko2/users_helper.rb +4 -0
  55. data/app/helpers/kuroko2/workers_helper.rb +4 -0
  56. data/app/jobs/kuroko2/application_job.rb +4 -0
  57. data/app/mailers/kuroko2/application_mailer.rb +6 -0
  58. data/app/mailers/kuroko2/notifications.rb +63 -0
  59. data/app/models/concerns/kuroko2/table_name_customizable.rb +16 -0
  60. data/app/models/kuroko2/admin_assignment.rb +6 -0
  61. data/app/models/kuroko2/api/application_resource.rb +10 -0
  62. data/app/models/kuroko2/api/job_instance_resource.rb +7 -0
  63. data/app/models/kuroko2/application_record.rb +5 -0
  64. data/app/models/kuroko2/execution.rb +55 -0
  65. data/app/models/kuroko2/job_definition.rb +147 -0
  66. data/app/models/kuroko2/job_definition_tag.rb +6 -0
  67. data/app/models/kuroko2/job_instance.rb +118 -0
  68. data/app/models/kuroko2/job_schedule.rb +93 -0
  69. data/app/models/kuroko2/job_suspend_schedule.rb +35 -0
  70. data/app/models/kuroko2/log.rb +3 -0
  71. data/app/models/kuroko2/memory_consumption_log.rb +49 -0
  72. data/app/models/kuroko2/memory_expectancy.rb +21 -0
  73. data/app/models/kuroko2/process_signal.rb +14 -0
  74. data/app/models/kuroko2/star.rb +6 -0
  75. data/app/models/kuroko2/tag.rb +8 -0
  76. data/app/models/kuroko2/tick.rb +12 -0
  77. data/app/models/kuroko2/token.rb +101 -0
  78. data/app/models/kuroko2/user.rb +48 -0
  79. data/app/models/kuroko2/worker.rb +12 -0
  80. data/app/views/kaminari/history/_paginator.html.slim +15 -0
  81. data/app/views/kaminari/list/_paginator.html.slim +17 -0
  82. data/app/views/kuroko2/dashboard/_taglist.html.slim +13 -0
  83. data/app/views/kuroko2/dashboard/index.html.slim +49 -0
  84. data/app/views/kuroko2/execution_logs/index.json.jbuilder +16 -0
  85. data/app/views/kuroko2/executions/index.html.slim +35 -0
  86. data/app/views/kuroko2/job_definition_stats/execution_time.json.jbuilder +13 -0
  87. data/app/views/kuroko2/job_definition_stats/index.html.slim +48 -0
  88. data/app/views/kuroko2/job_definition_stats/memory.json.jbuilder +10 -0
  89. data/app/views/kuroko2/job_definitions/_alert.html.slim +7 -0
  90. data/app/views/kuroko2/job_definitions/_form.html.slim +93 -0
  91. data/app/views/kuroko2/job_definitions/_list.html.slim +26 -0
  92. data/app/views/kuroko2/job_definitions/_search_results.html.slim +51 -0
  93. data/app/views/kuroko2/job_definitions/_taglist.html.slim +13 -0
  94. data/app/views/kuroko2/job_definitions/edit.html.slim +15 -0
  95. data/app/views/kuroko2/job_definitions/index.html.slim +11 -0
  96. data/app/views/kuroko2/job_definitions/new.html.slim +12 -0
  97. data/app/views/kuroko2/job_definitions/show.html.slim +90 -0
  98. data/app/views/kuroko2/job_instances/_instance.html.slim +42 -0
  99. data/app/views/kuroko2/job_instances/index.html.slim +58 -0
  100. data/app/views/kuroko2/job_instances/show.html.slim +19 -0
  101. data/app/views/kuroko2/job_instances/working.html.slim +37 -0
  102. data/app/views/kuroko2/job_schedules/index.html.slim +32 -0
  103. data/app/views/kuroko2/job_suspend_schedules/index.html.slim +27 -0
  104. data/app/views/kuroko2/job_timelines/dataset.json.jbuilder +11 -0
  105. data/app/views/kuroko2/job_timelines/index.html.slim +31 -0
  106. data/app/views/kuroko2/kaminari/history/_paginator.html.slim +15 -0
  107. data/app/views/kuroko2/kaminari/list/_paginator.html.slim +17 -0
  108. data/app/views/kuroko2/layouts/application.html.slim +68 -0
  109. data/app/views/kuroko2/logs/index.html.slim +33 -0
  110. data/app/views/kuroko2/notifications/executor_not_assigned.text.erb +22 -0
  111. data/app/views/kuroko2/notifications/job_failure.html.slim +21 -0
  112. data/app/views/kuroko2/notifications/job_failure.text.erb +18 -0
  113. data/app/views/kuroko2/notifications/notify_long_elapsed_time.text.erb +9 -0
  114. data/app/views/kuroko2/notifications/process_absence.text.erb +22 -0
  115. data/app/views/kuroko2/notifications/remind_failure.html.slim +21 -0
  116. data/app/views/kuroko2/notifications/remind_failure.text.erb +10 -0
  117. data/app/views/kuroko2/sessions/new.html.slim +20 -0
  118. data/app/views/kuroko2/tokens/index.html.slim +42 -0
  119. data/app/views/kuroko2/users/index.html.slim +55 -0
  120. data/app/views/kuroko2/users/show.html.slim +76 -0
  121. data/app/views/kuroko2/workers/index.html.slim +40 -0
  122. data/app/views/layouts/kuroko2/application.html.slim +72 -0
  123. data/app/views/layouts/mailer.html.erb +13 -0
  124. data/app/views/layouts/mailer.text.erb +1 -0
  125. data/bin/cleanup_old_instances.rb +9 -0
  126. data/bin/remind_failure.rb +5 -0
  127. data/config/initializers/000_kuroko2.rb +16 -0
  128. data/config/initializers/assets.rb +9 -0
  129. data/config/initializers/garage.rb +2 -0
  130. data/config/initializers/kaminari_config.rb +3 -0
  131. data/config/initializers/omniauth.rb +5 -0
  132. data/config/locales/en.yml +28 -0
  133. data/config/routes.rb +54 -0
  134. data/db/migrate/001_create_job_definitions.rb +22 -0
  135. data/db/migrate/002_create_job_instances.rb +17 -0
  136. data/db/migrate/003_create_job_schedules.rb +14 -0
  137. data/db/migrate/004_create_ticks.rb +7 -0
  138. data/db/migrate/005_create_logs.rb +13 -0
  139. data/db/migrate/006_create_tokens.rb +21 -0
  140. data/db/migrate/007_create_executions.rb +26 -0
  141. data/db/migrate/008_create_process_signals.rb +15 -0
  142. data/db/migrate/009_create_users.rb +20 -0
  143. data/db/migrate/010_create_admin_assignments.rb +12 -0
  144. data/db/migrate/011_create_stars.rb +12 -0
  145. data/db/migrate/012_create_workers.rb +15 -0
  146. data/db/migrate/018_create_job_definition_tags.rb +13 -0
  147. data/db/migrate/019_create_tags.rb +11 -0
  148. data/db/migrate/021_create_memory_expectancies.rb +17 -0
  149. data/db/migrate/025_create_job_suspend_schedules.rb +12 -0
  150. data/lib/kuroko2/command/executor.rb +62 -0
  151. data/lib/kuroko2/command/kill.rb +24 -0
  152. data/lib/kuroko2/command/monitor.rb +109 -0
  153. data/lib/kuroko2/command/shell.rb +163 -0
  154. data/lib/kuroko2/configuration.rb +19 -0
  155. data/lib/kuroko2/engine.rb +59 -0
  156. data/lib/kuroko2/execution_logger/cloud_watch_logs.rb +92 -0
  157. data/lib/kuroko2/execution_logger/void.rb +13 -0
  158. data/lib/kuroko2/execution_logger.rb +20 -0
  159. data/lib/kuroko2/memory_sampler.rb +50 -0
  160. data/lib/kuroko2/return_to_validator.rb +14 -0
  161. data/lib/kuroko2/servers/base.rb +30 -0
  162. data/lib/kuroko2/servers/command_executor.rb +27 -0
  163. data/lib/kuroko2/servers/job_scheduler.rb +25 -0
  164. data/lib/kuroko2/servers/workflow_processor.rb +25 -0
  165. data/lib/kuroko2/util/logger.rb +19 -0
  166. data/lib/kuroko2/util/rails_logger_formatter.rb +9 -0
  167. data/lib/kuroko2/version.rb +3 -0
  168. data/lib/kuroko2/workflow/assertion_error.rb +6 -0
  169. data/lib/kuroko2/workflow/engine.rb +141 -0
  170. data/lib/kuroko2/workflow/engine_error.rb +6 -0
  171. data/lib/kuroko2/workflow/node.rb +124 -0
  172. data/lib/kuroko2/workflow/notifier/concerns/chat_message_builder.rb +39 -0
  173. data/lib/kuroko2/workflow/notifier/hipchat.rb +88 -0
  174. data/lib/kuroko2/workflow/notifier/mail.rb +44 -0
  175. data/lib/kuroko2/workflow/notifier/slack.rb +121 -0
  176. data/lib/kuroko2/workflow/notifier.rb +31 -0
  177. data/lib/kuroko2/workflow/processor.rb +40 -0
  178. data/lib/kuroko2/workflow/scheduler.rb +42 -0
  179. data/lib/kuroko2/workflow/script_parser.rb +66 -0
  180. data/lib/kuroko2/workflow/shell_scanner.rb +34 -0
  181. data/lib/kuroko2/workflow/syntax_error.rb +6 -0
  182. data/lib/kuroko2/workflow/task/auto_skip_error.rb +20 -0
  183. data/lib/kuroko2/workflow/task/base.rb +32 -0
  184. data/lib/kuroko2/workflow/task/env.rb +45 -0
  185. data/lib/kuroko2/workflow/task/execute.rb +128 -0
  186. data/lib/kuroko2/workflow/task/expected_time.rb +9 -0
  187. data/lib/kuroko2/workflow/task/fork.rb +45 -0
  188. data/lib/kuroko2/workflow/task/kuroko_runner.rb +24 -0
  189. data/lib/kuroko2/workflow/task/noop.rb +13 -0
  190. data/lib/kuroko2/workflow/task/queue.rb +27 -0
  191. data/lib/kuroko2/workflow/task/rails_env.rb +24 -0
  192. data/lib/kuroko2/workflow/task/sequence.rb +13 -0
  193. data/lib/kuroko2/workflow/task/sleep.rb +29 -0
  194. data/lib/kuroko2/workflow/task/sub_process.rb +53 -0
  195. data/lib/kuroko2/workflow/task/time_base.rb +44 -0
  196. data/lib/kuroko2/workflow/task/timeout.rb +9 -0
  197. data/lib/kuroko2/workflow/task/wait.rb +143 -0
  198. data/lib/kuroko2.rb +26 -0
  199. data/lib/tasks/kuroko2_tasks.rake +4 -0
  200. data/spec/command/kill_spec.rb +20 -0
  201. data/spec/command/monitor_spec.rb +68 -0
  202. data/spec/command/shell_spec.rb +87 -0
  203. data/spec/controllers/dashboard_controller_spec.rb +5 -0
  204. data/spec/controllers/executions_controller_spec.rb +23 -0
  205. data/spec/controllers/job_definition_stats_controller_spec.rb +95 -0
  206. data/spec/controllers/job_definitions_controller_spec.rb +89 -0
  207. data/spec/controllers/job_instances_controller_spec.rb +62 -0
  208. data/spec/controllers/job_schedules_controller_spec.rb +39 -0
  209. data/spec/controllers/job_suspend_schedules_controller_spec.rb +39 -0
  210. data/spec/controllers/job_timelines_controller_spec.rb +114 -0
  211. data/spec/controllers/logs_controller_spec.rb +5 -0
  212. data/spec/controllers/sessions_controller_spec.rb +58 -0
  213. data/spec/controllers/stars_controller_spec.rb +28 -0
  214. data/spec/controllers/tokens_controller_spec.rb +5 -0
  215. data/spec/controllers/users_controller_spec.rb +51 -0
  216. data/spec/controllers/workers_controller_spec.rb +5 -0
  217. data/spec/dummy/Rakefile +6 -0
  218. data/spec/dummy/app/assets/config/manifest.js +5 -0
  219. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  220. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  221. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  222. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  223. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  224. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  225. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  226. data/spec/dummy/app/jobs/application_job.rb +2 -0
  227. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  228. data/spec/dummy/app/models/application_record.rb +3 -0
  229. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  230. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  231. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  232. data/spec/dummy/bin/bundle +3 -0
  233. data/spec/dummy/bin/rails +4 -0
  234. data/spec/dummy/bin/rake +4 -0
  235. data/spec/dummy/bin/setup +34 -0
  236. data/spec/dummy/bin/update +29 -0
  237. data/spec/dummy/config/application.rb +25 -0
  238. data/spec/dummy/config/boot.rb +3 -0
  239. data/spec/dummy/config/cable.yml +9 -0
  240. data/spec/dummy/config/database.yml +29 -0
  241. data/spec/dummy/config/environment.rb +5 -0
  242. data/spec/dummy/config/environments/development.rb +54 -0
  243. data/spec/dummy/config/environments/production.rb +86 -0
  244. data/spec/dummy/config/environments/test.rb +42 -0
  245. data/spec/dummy/config/initializers/application_controller_renderer.rb +6 -0
  246. data/spec/dummy/config/initializers/assets.rb +11 -0
  247. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  248. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  249. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  250. data/spec/dummy/config/initializers/inflections.rb +16 -0
  251. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  252. data/spec/dummy/config/initializers/new_framework_defaults.rb +23 -0
  253. data/spec/dummy/config/initializers/session_store.rb +3 -0
  254. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  255. data/spec/dummy/config/kuroko2.yml +35 -0
  256. data/spec/dummy/config/locales/en.yml +23 -0
  257. data/spec/dummy/config/puma.rb +47 -0
  258. data/spec/dummy/config/routes.rb +3 -0
  259. data/spec/dummy/config/secrets.yml +22 -0
  260. data/spec/dummy/config/spring.rb +6 -0
  261. data/spec/dummy/config.ru +5 -0
  262. data/spec/dummy/db/schema.rb +217 -0
  263. data/spec/dummy/lib/dummy_extention.rb +14 -0
  264. data/spec/dummy/lib/kuroko2/workflow/task/custom_task1.rb +13 -0
  265. data/spec/dummy/public/404.html +67 -0
  266. data/spec/dummy/public/422.html +67 -0
  267. data/spec/dummy/public/500.html +66 -0
  268. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  269. data/spec/dummy/public/apple-touch-icon.png +0 -0
  270. data/spec/dummy/public/favicon.ico +0 -0
  271. data/spec/execution_logger/cloud_watch_logs_spec.rb +95 -0
  272. data/spec/factories/execution_factory.rb +13 -0
  273. data/spec/factories/job_definition_factory.rb +21 -0
  274. data/spec/factories/job_instance_factory.rb +4 -0
  275. data/spec/factories/job_schedule_factory.rb +10 -0
  276. data/spec/factories/job_suspend_schedule_factory.rb +8 -0
  277. data/spec/factories/memory_expectancy_factory.rb +5 -0
  278. data/spec/factories/process_signal_factory.rb +4 -0
  279. data/spec/factories/star_factory.rb +4 -0
  280. data/spec/factories/tick_factory.rb +4 -0
  281. data/spec/factories/token_factory.rb +10 -0
  282. data/spec/factories/user_factory.rb +11 -0
  283. data/spec/factories/worker_factory.rb +8 -0
  284. data/spec/features/dashborad_spec.rb +82 -0
  285. data/spec/features/job_definition_spec.rb +74 -0
  286. data/spec/features/job_instance_spec.rb +94 -0
  287. data/spec/features/sign_in_and_out_spec.rb +17 -0
  288. data/spec/features/users_spec.rb +90 -0
  289. data/spec/features/workers_spec.rb +44 -0
  290. data/spec/helpers/executions_helper_spec.rb +4 -0
  291. data/spec/helpers/job_definition_helper_spec.rb +42 -0
  292. data/spec/helpers/job_schedules_helper_spec.rb +4 -0
  293. data/spec/helpers/logs_helper_spec.rb +4 -0
  294. data/spec/helpers/tokens_helper_spec.rb +4 -0
  295. data/spec/helpers/users_helper_spec.rb +4 -0
  296. data/spec/helpers/workers_helper_spec.rb +4 -0
  297. data/spec/mailers/notifications_spec.rb +54 -0
  298. data/spec/memory_sampler_spec.rb +11 -0
  299. data/spec/models/admin_assignment_spec.rb +4 -0
  300. data/spec/models/execution_spec.rb +26 -0
  301. data/spec/models/job_definition_spec.rb +163 -0
  302. data/spec/models/job_instance_spec.rb +115 -0
  303. data/spec/models/job_schedule_spec.rb +121 -0
  304. data/spec/models/job_suspend_schedule_spec.rb +32 -0
  305. data/spec/models/memory_consumption_log_spec.rb +50 -0
  306. data/spec/models/memory_expectancy_spec.rb +26 -0
  307. data/spec/models/star_spec.rb +4 -0
  308. data/spec/models/tick_spec.rb +23 -0
  309. data/spec/models/token_spec.rb +54 -0
  310. data/spec/models/user_spec.rb +17 -0
  311. data/spec/models/worker_spec.rb +5 -0
  312. data/spec/rails_helper.rb +81 -0
  313. data/spec/requests/api/job_instances_spec.rb +96 -0
  314. data/spec/requests/api/stats_spec.rb +45 -0
  315. data/spec/return_to_validator_spec.rb +28 -0
  316. data/spec/settings_spec.rb +10 -0
  317. data/spec/spec_helper.rb +49 -0
  318. data/spec/support/feature_sign_in_helper.rb +31 -0
  319. data/spec/support/sign_in_helper.rb +5 -0
  320. data/spec/support/wait_for_ajax.rb +11 -0
  321. data/spec/workflow/engine_spec.rb +241 -0
  322. data/spec/workflow/node_spec.rb +62 -0
  323. data/spec/workflow/notifier/hipchat_spec.rb +117 -0
  324. data/spec/workflow/notifier/mail_spec.rb +86 -0
  325. data/spec/workflow/notifier/slack_spec.rb +110 -0
  326. data/spec/workflow/script_parser_spec.rb +119 -0
  327. data/spec/workflow/shell_scanner_spec.rb +47 -0
  328. data/spec/workflow/task/auto_skip_error_spec.rb +35 -0
  329. data/spec/workflow/task/env_spec.rb +47 -0
  330. data/spec/workflow/task/execute_spec.rb +127 -0
  331. data/spec/workflow/task/expected_time_spec.rb +52 -0
  332. data/spec/workflow/task/fork_spec.rb +30 -0
  333. data/spec/workflow/task/queue_spec.rb +45 -0
  334. data/spec/workflow/task/rails_env_spec.rb +30 -0
  335. data/spec/workflow/task/sleep_spec.rb +22 -0
  336. data/spec/workflow/task/sub_process_spec.rb +32 -0
  337. data/spec/workflow/task/wait_spec.rb +162 -0
  338. metadata +1038 -0
@@ -0,0 +1,6 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ class AssertionError < EngineError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,141 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ class Engine
4
+ def process_all
5
+ Token.processable.each do |token|
6
+ process(token)
7
+ end
8
+ end
9
+
10
+ def process(token)
11
+ unless token.working? || token.waiting?
12
+ Kuroko2.logger.info { "(token #{token.uuid}) Skip since current status marked as '#{token.status_name}'." }
13
+
14
+ return
15
+ end
16
+
17
+ token.with_lock { process_with_lock(token) }
18
+ end
19
+
20
+ def retry(token)
21
+ token.with_lock do
22
+ node = extract_node(token)
23
+
24
+ message = "(token #{token.uuid}) Retry current node: '#{node.type}: #{node.option}'"
25
+ token.job_instance.update_column(:error_at, nil)
26
+ token.job_instance.logs.info(message)
27
+
28
+ token.mark_as_working
29
+ token.save!
30
+
31
+ Kuroko2.logger.info(message)
32
+
33
+ Notifier.notify(:working, token.job_instance)
34
+ end
35
+ end
36
+
37
+ def skip(token)
38
+ token.with_lock do
39
+ node = extract_node(token)
40
+
41
+ message = "(token #{token.uuid}) Skip current node: '#{node.type}: #{node.option}'"
42
+ token.job_instance.update_column(:error_at, nil)
43
+ token.job_instance.logs.info(message)
44
+
45
+ token.mark_as_working
46
+ process_next(node.next, token)
47
+
48
+ token.save! unless token.destroyed?
49
+
50
+ Kuroko2.logger.info(message)
51
+
52
+ Notifier.notify(:working, token.job_instance)
53
+ end
54
+ end
55
+
56
+ def failure(token)
57
+ message = "(token #{token.uuid}) Mark as failure."
58
+
59
+ token.job_instance.logs.error(message)
60
+ token.job_instance.touch(:error_at)
61
+ token.mark_as_failure
62
+
63
+ Kuroko2.logger.info(message)
64
+
65
+ Notifier.notify(:failure, token.job_instance)
66
+
67
+ if token.context['AUTO_SKIP_ERROR']
68
+ skip(token)
69
+ end
70
+ end
71
+
72
+ private
73
+
74
+ def execute_task(node, token)
75
+ result = node.execute(token)
76
+
77
+ case result
78
+ when :next
79
+ process_next(node.next, token)
80
+ when :next_sibling
81
+ process_next(node.next_sibling, token)
82
+ when :pass
83
+ # Do nothing
84
+ when :failure
85
+ failure(token)
86
+ end
87
+ rescue KeyError => e
88
+ raise EngineError.new(e.message)
89
+ end
90
+
91
+ def process_next(node, token)
92
+ if node
93
+ message = "(token #{token.uuid}) Current node is '#{token.path}'."
94
+
95
+ token.path = node.path
96
+ token.job_instance.logs.info(message)
97
+
98
+ Kuroko2.logger.info(message)
99
+ else
100
+ message = "(token #{token.uuid}) Marked as 'finished'."
101
+
102
+ token.job_instance.logs.info(message)
103
+ token.mark_as_finished
104
+ unless token.parent
105
+ token.job_instance.touch(:finished_at)
106
+ token.destroy!
107
+ end
108
+
109
+ Kuroko2.logger.info(message)
110
+
111
+ Notifier.notify(:finished, token.job_instance)
112
+ end
113
+ end
114
+
115
+ def process_with_lock(token)
116
+ node = extract_node(token)
117
+
118
+ execute_task(node, token)
119
+ rescue EngineError => e
120
+ message = "#{e.message}\n" + e.backtrace.map { |trace| " #{trace}" }.join("\n")
121
+
122
+ token.mark_as_critical(e)
123
+ token.job_instance.logs.error("(token #{token.uuid}) #{message}")
124
+ token.job_instance.touch(:canceled_at)
125
+
126
+ Token.delete_all(job_definition: token.job_definition)
127
+ token.job_instance.logs.warn("(token #{token.uuid}) This job is canceled.")
128
+
129
+ Kuroko2.logger.error(message)
130
+ Notifier.notify(:critical, token.job_instance)
131
+ ensure
132
+ token.save! unless token.destroyed?
133
+ end
134
+
135
+ def extract_node(token)
136
+ root = ScriptParser.new(token.script).parse(validate: false)
137
+ root.find(token.path)
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,6 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ class EngineError < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,124 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ class Node
4
+ PATH_REGEXP = %r(\A(?:/|(?:/\d+-[a-z0-9_]+)+)\z)
5
+ TASK_REGISTORY = {
6
+ root: Task::Sequence,
7
+ noop: Task::Noop,
8
+ sequence: Task::Sequence,
9
+ auto_skip_error: Task::AutoSkipError,
10
+ fork: Task::Fork,
11
+ env: Task::Env,
12
+ execute: Task::Execute,
13
+ queue: Task::Queue,
14
+ sub_process: Task::SubProcess,
15
+ subprocess: Task::SubProcess,
16
+ timeout: Task::Timeout,
17
+ expected_time: Task::ExpectedTime,
18
+ wait: Task::Wait,
19
+ sleep: Task::Sleep,
20
+ rails_env: Task::RailsEnv,
21
+ kuroko_runner: Task::KurokoRunner,
22
+ }
23
+
24
+ attr_reader :type, :option, :children
25
+ attr_accessor :parent
26
+
27
+ def self.register(key: nil, klass:)
28
+ key ||= klass.to_s.demodulize.underscore.to_sym
29
+
30
+ unless TASK_REGISTORY.has_key?(key)
31
+ TASK_REGISTORY.store(key, klass)
32
+ else
33
+ Kuroko2.logger.warn("Unable to add '#{klass}' to task registory. '#{TASK_REGISTORY[key]}' is already registered.")
34
+ end
35
+ end
36
+
37
+ def self.deregister(key)
38
+ TASK_REGISTORY.delete(key)
39
+ end
40
+
41
+ def initialize(type, option = nil)
42
+ @type = type.to_sym
43
+ @task_klass = TASK_REGISTORY.fetch(@type, nil)
44
+ @option = option.try(:strip)
45
+ @parent = nil
46
+ @children = []
47
+
48
+ raise AssertionError, "`#{@type}` is not registered in task repository." unless @task_klass
49
+ end
50
+
51
+ def append_child(child)
52
+ child.parent = self
53
+ @children << child
54
+ end
55
+
56
+ def execute(token)
57
+ Kuroko2.logger.debug { "(token #{token.uuid}) Execute #{@type} with option '#{@option}'." }
58
+ @task_klass.new(self, token).execute.tap do |result|
59
+ Kuroko2.logger.debug("(token #{token.uuid}) Result is '#{result}'.")
60
+ end
61
+ end
62
+
63
+ def find(path)
64
+ raise AssertionError, "path query('#{path}') is invalid." unless PATH_REGEXP === path
65
+
66
+ query = path.split('/')
67
+ query.shift # drop first empty string.
68
+
69
+ traverse(query)
70
+ end
71
+
72
+ def next(index = 0)
73
+ if (child = children[index])
74
+ child
75
+ else
76
+ next_sibling
77
+ end
78
+ end
79
+
80
+ def next_sibling
81
+ if parent
82
+ parent.next(current_index + 1)
83
+ else
84
+ nil
85
+ end
86
+ end
87
+
88
+ def path
89
+ if parent
90
+ parent.path + "/#{current_index}-#{type}"
91
+ else
92
+ ''
93
+ end
94
+ end
95
+
96
+ def to_script(indent = 0)
97
+ "#{' ' * indent}#{type}: #{option}\n" + children.map { |child| child.to_script(indent + 1) }.join
98
+ end
99
+
100
+ def validate_all
101
+ @task_klass.new(self, nil).validate
102
+ @children.each do |child|
103
+ child.validate_all
104
+ end
105
+ end
106
+
107
+ protected
108
+
109
+ def current_index
110
+ @_current_index = parent.children.index(self)
111
+ end
112
+
113
+ def traverse(query)
114
+ return self if query.empty?
115
+
116
+ first = query.shift
117
+ index, _ = first.split('-')
118
+
119
+ @children[index.to_i].traverse(query)
120
+ end
121
+
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,39 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Notifier
4
+ module Concerns
5
+ class ChatMessageBuilder
6
+ def initialize(instance)
7
+ @instance = instance
8
+ @definition = instance.job_definition
9
+ end
10
+
11
+ def failure_text
12
+ "Failed to execute '#{@definition.name}'"
13
+ end
14
+
15
+ def finished_text
16
+ "Finished to execute '#{@definition.name}'"
17
+ end
18
+
19
+ def long_elapsed_time_text
20
+ "The running time is longer than expected '#{@definition.name}'."
21
+ end
22
+
23
+ def additional_text
24
+ "Failed to execute '#{@definition.name}' #{@definition.hipchat_additional_text}"
25
+ end
26
+
27
+ def job_instance_path
28
+ Kuroko2::Engine.routes.url_helpers.job_definition_job_instance_url(
29
+ @definition,
30
+ @instance,
31
+ host: Kuroko2.config.url_host,
32
+ protocol: Kuroko2.config.url_scheme,
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,88 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Notifier
4
+ class Hipchat
5
+ attr_reader :hipchat, :message_builder
6
+
7
+ USER_NAME = 'kuroko2'
8
+
9
+ def initialize(instance)
10
+ @instance = instance
11
+ @definition = instance.job_definition
12
+ @hipchat = Kuroko2.config.notifiers.hipchat.api_token
13
+ @message_builder = Workflow::Notifier::Concerns::ChatMessageBuilder.new(instance)
14
+ end
15
+
16
+ def notify_working
17
+ # do nothing
18
+ end
19
+
20
+ def notify_cancellation
21
+ if @definition.notify_cancellation
22
+ message = build_message(level: 'WARNING', text: message_builder.failure_text)
23
+ message << "<br>"
24
+ message << @instance.logs.last(2).first.message
25
+
26
+ send_to_hipchat(message, color: 'yellow')
27
+ end
28
+ end
29
+
30
+ def notify_failure
31
+ message = build_message(level: 'FAILURE', text: message_builder.failure_text)
32
+ message << "<br>"
33
+ message << @instance.logs.last(2).first.message
34
+
35
+ send_to_hipchat(message, color: 'red', notify: true)
36
+ send_additional_text_to_hipchat
37
+ end
38
+
39
+ def notify_critical
40
+ message = build_message(level: 'CRITICAL', text: message_builder.failure_text)
41
+ message << "<br>"
42
+ message << @instance.logs.last(2).first.message
43
+
44
+ send_to_hipchat(message, color: 'red', notify: true)
45
+ send_additional_text_to_hipchat
46
+ end
47
+
48
+ def notify_finished
49
+ if @definition.hipchat_notify_finished?
50
+ message = build_message(level: 'SUCCESS', text: message_builder.finished_text)
51
+ send_to_hipchat(message)
52
+ end
53
+ end
54
+
55
+ def notify_long_elapsed_time
56
+ message = build_message(level: 'WARNING', text: message_builder.long_elapsed_time_text)
57
+ send_to_hipchat(message, color: 'red')
58
+ end
59
+
60
+ private
61
+
62
+ def send_to_hipchat(message, color: 'green', notify: false, format: 'html')
63
+ if notify_hipchat?
64
+
65
+ hipchat[@definition.hipchat_room].send(USER_NAME, message, color: color, notify: notify, message_format: format)
66
+ end
67
+ end
68
+
69
+ def build_message(level: , text:)
70
+ message = "<b>[#{level}]</b> "
71
+ message << text
72
+ message << "(<a href='#{message_builder.job_instance_path}'>Open</a>)"
73
+ end
74
+
75
+ def send_additional_text_to_hipchat
76
+ if @definition.hipchat_additional_text.present?
77
+ message = message_builder.additional_text
78
+ send_to_hipchat(message, color: 'red', notify: true, format: 'text')
79
+ end
80
+ end
81
+
82
+ def notify_hipchat?
83
+ @definition.hipchat_room.present?
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,44 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Notifier
4
+ class Mail
5
+ def initialize(job_instance)
6
+ @job_instance = job_instance
7
+ @definition = job_instance.job_definition
8
+ end
9
+
10
+ def notify_working
11
+ # do nothing
12
+ end
13
+
14
+ def notify_cancellation
15
+ if @definition.notify_cancellation
16
+ deliver_job_failure
17
+ end
18
+ end
19
+
20
+ def notify_failure
21
+ deliver_job_failure
22
+ end
23
+
24
+ def notify_critical
25
+ deliver_job_failure
26
+ end
27
+
28
+ def notify_finished
29
+ # do nothing
30
+ end
31
+
32
+ def notify_long_elapsed_time
33
+ Kuroko2::Notifications.notify_long_elapsed_time(@job_instance).deliver_now
34
+ end
35
+
36
+ private
37
+
38
+ def deliver_job_failure
39
+ Kuroko2::Notifications.job_failure(@job_instance).deliver_now
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,121 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Notifier
4
+ class Slack
5
+ attr_reader :message_builder, :webhook_url
6
+
7
+ module LevelToColor
8
+ WARNING = 'warning'
9
+ FAILURE = 'danger'
10
+ CRITICAL = 'danger'
11
+ SUCCESS = 'good'
12
+ end
13
+
14
+ def initialize(instance)
15
+ @instance = instance
16
+ @definition = instance.job_definition
17
+ @message_builder = Workflow::Notifier::Concerns::ChatMessageBuilder.new(instance)
18
+ @webhook_url = Kuroko2.config.notifiers.slack.webhook_url
19
+ end
20
+
21
+ def notify_working
22
+ # do nothing
23
+ end
24
+
25
+ def notify_cancellation
26
+ if @definition.notify_cancellation
27
+ send_attachment_message_to_slack(
28
+ level: 'WARNING',
29
+ text: message_builder.failure_text,
30
+ body: @instance.logs.last(2).first.message,
31
+ )
32
+ end
33
+ end
34
+
35
+ def notify_failure
36
+ send_attachment_message_to_slack(
37
+ level: 'FAILURE',
38
+ text: message_builder.failure_text,
39
+ body: @instance.logs.last(2).first.message,
40
+ )
41
+
42
+ send_additional_text_to_slack
43
+ end
44
+
45
+ def notify_critical
46
+ send_attachment_message_to_slack(
47
+ level: 'CRITICAL',
48
+ text: message_builder.failure_text,
49
+ body: @instance.logs.last(2).first.message,
50
+ )
51
+
52
+ send_additional_text_to_slack
53
+ end
54
+
55
+ def notify_finished
56
+ if @definition.hipchat_notify_finished?
57
+ send_attachment_message_to_slack(
58
+ level: 'SUCCESS',
59
+ text: message_builder.finished_text,
60
+ )
61
+ end
62
+ end
63
+
64
+ def notify_long_elapsed_time
65
+ send_attachment_message_to_slack(
66
+ level: 'WARNING',
67
+ text: message_builder.long_elapsed_time_text,
68
+ )
69
+ end
70
+
71
+ private
72
+
73
+ def send_attachment_message_to_slack(level: , text: , body: nil)
74
+ return false unless @definition.slack_channel.present?
75
+
76
+ send_to_slack(
77
+ channel: @definition.slack_channel,
78
+ link_names: 1,
79
+ attachments: [
80
+ {
81
+ pretext: "[#{level}] #{text}",
82
+ title: "##{@definition.id} #{@definition.name}",
83
+ title_link: message_builder.job_instance_path,
84
+ text: body,
85
+ fallback: "[#{level}] #{text} #{message_builder.job_instance_path}",
86
+ color: LevelToColor.const_get(level),
87
+ }
88
+ ]
89
+ )
90
+ end
91
+
92
+ def send_to_slack(payload)
93
+ url = URI.parse(webhook_url)
94
+ conn = Faraday.new(:url => "#{url.scheme}://#{url.host}") do |faraday|
95
+ faraday.adapter Faraday.default_adapter
96
+ end
97
+
98
+ response = conn.post do |req|
99
+ req.url url.path
100
+ req.headers['Content-Type'] = 'application/json'
101
+ req.body = payload.to_json
102
+ end
103
+
104
+ unless response.success?
105
+ Kuroko2.logger.fatal("Failure sending message to Slack: status=#{response.status} body=#{response.body}")
106
+ end
107
+ end
108
+
109
+ def send_additional_text_to_slack
110
+ if @definition.slack_channel.present? && @definition.hipchat_additional_text.present?
111
+ send_to_slack(
112
+ channel: @definition.slack_channel,
113
+ text: message_builder.additional_text,
114
+ link_names: 1
115
+ )
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,31 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Notifier
4
+ NOTIFY_IN_THREAD = !Rails.env.test?
5
+
6
+ def self.notify(method, job_instance)
7
+ Kuroko2.config.notifiers.keys.each do |notifier_name|
8
+ notifier = const_get(notifier_name.camelize, false)
9
+ if NOTIFY_IN_THREAD
10
+ Thread.new { notify_with_notifier(job_instance, method, notifier) }
11
+ else
12
+ # for test
13
+ notify_with_notifier(job_instance, method, notifier)
14
+ end
15
+ end
16
+ end
17
+
18
+ def self.notify_with_notifier(job_instance, method, notifier)
19
+ begin
20
+ ActiveRecord::Base.connection_pool.with_connection do
21
+ notifier.new(job_instance).send(:"notify_#{method}")
22
+ end
23
+ rescue Exception => e
24
+ Kuroko2.logger.warn("Failure to notify #{method} with #{notifier} for '#{job_instance.job_definition.name}'. #{e.class}: #{e.message}")
25
+ end
26
+ end
27
+
28
+ private_class_method :notify_with_notifier
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,40 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Processor
4
+ def initialize
5
+ @hostname = Socket.gethostname
6
+
7
+ @stop = ServerEngine::BlockingFlag.new
8
+ @processing = ServerEngine::BlockingFlag.new
9
+
10
+ @workflow = Workflow::Engine.new
11
+ end
12
+
13
+ def run
14
+ Kuroko2.logger = logger
15
+ Kuroko2.logger.info "[#{@hostname}-#{worker_id}] Start Workflow::Processor"
16
+
17
+ until @stop.wait(1.0)
18
+ unless @processing.set?
19
+ begin
20
+ @processing.set!
21
+ @workflow.process_all
22
+ @processing.reset!
23
+ end
24
+ end
25
+ end
26
+ rescue Exception => e
27
+ Kuroko2.logger.fatal("[#{@hostname}-#{worker_id}] #{e.class}: #{e.message}\n" +
28
+ e.backtrace.map { |trace| " #{trace}" }.join("\n"))
29
+
30
+ raise e
31
+ end
32
+
33
+ def stop
34
+ Kuroko2.logger.info "[#{@hostname}-#{worker_id}] Stop Workflow::Processor"
35
+
36
+ @stop.set!
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ module Kuroko2
2
+ module Workflow
3
+ module Scheduler
4
+ def initialize
5
+ @hostname = Socket.gethostname
6
+
7
+ @stop = ServerEngine::BlockingFlag.new
8
+ @processing = ServerEngine::BlockingFlag.new
9
+ end
10
+
11
+ def run
12
+ Kuroko2.logger = logger
13
+ Kuroko2.logger.info "[#{@hostname}-#{worker_id}] Start Workflow::Scheduler"
14
+
15
+ until @stop.wait(2.0)
16
+ unless @processing.set?
17
+ begin
18
+ @processing.set!
19
+ JobSchedule.transaction do
20
+ now = Time.now
21
+ last_scheduled_time = Tick.fetch_then_update(now)
22
+ JobSchedule.launch_scheduled_jobs!(last_scheduled_time, now)
23
+ end
24
+ @processing.reset!
25
+ end
26
+ end
27
+ end
28
+ rescue Exception => e
29
+ Kuroko2.logger.fatal("[#{@hostname}-#{worker_id}] #{e.class}: #{e.message}\n" +
30
+ e.backtrace.map { |trace| " #{trace}" }.join("\n"))
31
+
32
+ raise e
33
+ end
34
+
35
+ def stop
36
+ Kuroko2.logger.info "[#{@hostname}-#{worker_id}] Stop Workflow::Scheduler"
37
+
38
+ @stop.set!
39
+ end
40
+ end
41
+ end
42
+ end