tasks_scheduler 0.3.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Rakefile +3 -1
- data/app/assets/javascripts/tasks_scheduler.js +2 -109
- data/app/assets/javascripts/tasks_scheduler/alert.js +58 -0
- data/app/assets/javascripts/tasks_scheduler/status.js +58 -0
- data/app/assets/stylesheets/tasks_scheduler.scss +4 -0
- data/app/controllers/scheduled_tasks_controller.rb +5 -4
- data/app/controllers/tasks_scheduler_daemon_controller.rb +4 -3
- data/app/controllers/tasks_scheduler_daemon_controller/_download_log.rb +6 -0
- data/app/helpers/scheduled_tasks_helper.rb +2 -0
- data/app/models/scheduled_task.rb +16 -14
- data/app/models/scheduled_task/checker.rb +17 -5
- data/app/models/scheduled_task/log.rb +9 -6
- data/app/models/scheduled_task/runner.rb +9 -5
- data/app/models/scheduled_task/status.rb +8 -4
- data/config/initializers/assets.rb +3 -1
- data/config/routes.rb +2 -0
- data/db/migrate/20161122123828_create_scheduled_tasks.rb +5 -1
- data/db/migrate/20161123130153_add_status_attributes_to_scheduled_tasks.rb +5 -1
- data/db/migrate/20161124200712_add_pid_to_scheduled_tasks.rb +5 -1
- data/db/migrate/20161128163702_add_args_to_scheduled_tasks.rb +5 -1
- data/db/migrate/20180223155025_add_last_fail_status_to_scheduled_tasks.rb +5 -1
- data/db/migrate/20180302170138_add_enabled_to_scheduled_tasks.rb +5 -1
- data/exe/tasks_scheduler +3 -1
- data/exe/tasks_scheduler_run_task +3 -1
- data/lib/tasks_scheduler.rb +4 -8
- data/lib/tasks_scheduler/app_gem.rb +18 -0
- data/lib/tasks_scheduler/checker.rb +5 -2
- data/lib/tasks_scheduler/checker/log.rb +2 -0
- data/lib/tasks_scheduler/cron_parser_patch.rb +5 -1
- data/lib/tasks_scheduler/cron_scheduling_validator.rb +3 -0
- data/lib/tasks_scheduler/daemon.rb +20 -13
- data/lib/tasks_scheduler/engine.rb +5 -0
- data/lib/tasks_scheduler/version.rb +3 -1
- data/test/dummy/Rakefile +3 -1
- data/test/dummy/app/controllers/application_controller.rb +2 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/bin/bundle +3 -1
- data/test/dummy/bin/rails +3 -1
- data/test/dummy/bin/rake +2 -0
- data/test/dummy/bin/setup +3 -1
- data/test/dummy/config.ru +2 -0
- data/test/dummy/config/application.rb +3 -1
- data/test/dummy/config/boot.rb +4 -2
- data/test/dummy/config/environment.rb +3 -1
- data/test/dummy/config/environments/development.rb +2 -0
- data/test/dummy/config/environments/production.rb +2 -0
- data/test/dummy/config/environments/test.rb +2 -0
- data/test/dummy/config/initializers/assets.rb +2 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +1 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +2 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +2 -0
- data/test/dummy/config/initializers/inflections.rb +1 -0
- data/test/dummy/config/initializers/mime_types.rb +1 -0
- data/test/dummy/config/initializers/session_store.rb +2 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +2 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/fixtures/scheduled_tasks.yml +2 -0
- data/test/models/scheduled_task_test.rb +6 -4
- data/test/test_helper.rb +6 -4
- metadata +88 -32
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f297d8aab57fc0f8e97be84ea5ca71a1dda0623a9231f124866f066a65a96703
         | 
| 4 | 
            +
              data.tar.gz: be6470159ccbc01e0953b40f4560e1543b3a2687f75c17147fda86d492f0523f
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: cf3db7dde7ceb96ece05b878c725eda9bdd3642e0c7ec229f248de845aea79028454533eb49692cbe772d892daaef40cb6f08153606dd489f4b50786e0275d11
         | 
| 7 | 
            +
              data.tar.gz: cc9a7644b4447216bdc692eea253f39c9183ddc4f24b44e5994b61a84cad0bf093b96607f9d80cc7bb9c33b08ed38292394affc28b49a0a03a85890210f98b62
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            begin
         | 
| 2 4 | 
             
              require 'bundler/setup'
         | 
| 3 5 | 
             
            rescue LoadError
         | 
| @@ -14,7 +16,7 @@ RDoc::Task.new(:rdoc) do |rdoc| | |
| 14 16 | 
             
              rdoc.rdoc_files.include('lib/**/*.rb')
         | 
| 15 17 | 
             
            end
         | 
| 16 18 |  | 
| 17 | 
            -
            APP_RAKEFILE = File.expand_path(' | 
| 19 | 
            +
            APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
         | 
| 18 20 | 
             
            load 'rails/tasks/engine.rake'
         | 
| 19 21 |  | 
| 20 22 | 
             
            load 'rails/tasks/statistics.rake'
         | 
| @@ -4,112 +4,5 @@ | |
| 4 4 | 
             
            function TasksScheduler() {
         | 
| 5 5 | 
             
            }
         | 
| 6 6 |  | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            // Shortcut
         | 
| 11 | 
            -
            var _S = TasksScheduler.Status;
         | 
| 12 | 
            -
             | 
| 13 | 
            -
            _S.initialized = false;
         | 
| 14 | 
            -
             | 
| 15 | 
            -
            _S.init = function (url, interval_max) {
         | 
| 16 | 
            -
              if (!_S.initialized) {
         | 
| 17 | 
            -
                _S.initialized = true;
         | 
| 18 | 
            -
                _S.url = url;
         | 
| 19 | 
            -
                _S.interval_max = interval_max;
         | 
| 20 | 
            -
                _S.update();
         | 
| 21 | 
            -
              }
         | 
| 22 | 
            -
            };
         | 
| 23 | 
            -
             | 
| 24 | 
            -
            _S.content = function () {
         | 
| 25 | 
            -
              return $('#TaskScheduler_Status_Content');
         | 
| 26 | 
            -
            };
         | 
| 27 | 
            -
             | 
| 28 | 
            -
            _S.status = function () {
         | 
| 29 | 
            -
              return $('#TaskScheduler_Status_Status');
         | 
| 30 | 
            -
            };
         | 
| 31 | 
            -
             | 
| 32 | 
            -
            _S.update_status = function () {
         | 
| 33 | 
            -
              _S.status().html(
         | 
| 34 | 
            -
                "Updating in " + _S.interval + " seconds..."
         | 
| 35 | 
            -
              );
         | 
| 36 | 
            -
            };
         | 
| 37 | 
            -
             | 
| 38 | 
            -
            _S.check = function () {
         | 
| 39 | 
            -
              if (_S.interval <= 0) {
         | 
| 40 | 
            -
                _S.update();
         | 
| 41 | 
            -
              } else {
         | 
| 42 | 
            -
                _S.interval--;
         | 
| 43 | 
            -
                _S.update_status();
         | 
| 44 | 
            -
                setTimeout(_S.check, 1000);
         | 
| 45 | 
            -
              }
         | 
| 46 | 
            -
            };
         | 
| 47 | 
            -
             | 
| 48 | 
            -
            _S.update = function () {
         | 
| 49 | 
            -
              $.ajax({
         | 
| 50 | 
            -
                url: _S.url,
         | 
| 51 | 
            -
                success: function (result) {
         | 
| 52 | 
            -
                  _S.content().html(result);
         | 
| 53 | 
            -
                },
         | 
| 54 | 
            -
                complete: function (result) {
         | 
| 55 | 
            -
                  _S.interval = _S.interval_max + 1;
         | 
| 56 | 
            -
                  _S.last_update = new Date();
         | 
| 57 | 
            -
                  _S.check();
         | 
| 58 | 
            -
                }
         | 
| 59 | 
            -
              });
         | 
| 60 | 
            -
            };
         | 
| 61 | 
            -
             | 
| 62 | 
            -
            TasksScheduler.Alert = function () {
         | 
| 63 | 
            -
            };
         | 
| 64 | 
            -
             | 
| 65 | 
            -
            _A = TasksScheduler.Alert;
         | 
| 66 | 
            -
             | 
| 67 | 
            -
            _A.DEFAULT_REFRESH_INTERVAL = 5000;
         | 
| 68 | 
            -
            _A.DEFAULT_ELEMENT_SELECTOR = '#tasks_scheduler_alert';
         | 
| 69 | 
            -
            _A.CSS_CLASSES_PREFIX = 'alert_';
         | 
| 70 | 
            -
            _A.url = Routes.status_tasks_scheduler_daemon_path();
         | 
| 71 | 
            -
             | 
| 72 | 
            -
            _A.init = function (options) {
         | 
| 73 | 
            -
              options = typeof options !== 'undefined' ? options : {};
         | 
| 74 | 
            -
              $(document).ready(function () {
         | 
| 75 | 
            -
                _A.options = options;
         | 
| 76 | 
            -
                if (!_A.options.refresh_interval) {
         | 
| 77 | 
            -
                  _A.options.refresh_interval = _A.DEFAULT_REFRESH_INTERVAL;
         | 
| 78 | 
            -
                }
         | 
| 79 | 
            -
                if (!_A.options.element_selector) {
         | 
| 80 | 
            -
                  _A.options.element_selector = _A.DEFAULT_ELEMENT_SELECTOR;
         | 
| 81 | 
            -
                }
         | 
| 82 | 
            -
                _A.refresh();
         | 
| 83 | 
            -
              });
         | 
| 84 | 
            -
            };
         | 
| 85 | 
            -
             | 
| 86 | 
            -
            _A.setNextRefresh = function () {
         | 
| 87 | 
            -
              setTimeout(_A.refresh, _A.options.refresh_interval);
         | 
| 88 | 
            -
            };
         | 
| 89 | 
            -
             | 
| 90 | 
            -
            _A.refresh = function () {
         | 
| 91 | 
            -
              $.ajax({
         | 
| 92 | 
            -
                url: _A.url,
         | 
| 93 | 
            -
                success: function (result) {
         | 
| 94 | 
            -
                  var alert = $(_A.options.element_selector);
         | 
| 95 | 
            -
                  var pattern = new RegExp('(^|\\s)' + _A.CSS_CLASSES_PREFIX + "\\S+", 'g');
         | 
| 96 | 
            -
                  alert.removeClass (function (index, className) {
         | 
| 97 | 
            -
                    return (className.match (pattern) || []).join(' ');
         | 
| 98 | 
            -
                  });
         | 
| 99 | 
            -
                  alert.addClass(_A.resultToCssClass(result));
         | 
| 100 | 
            -
                },
         | 
| 101 | 
            -
                complete: function (result) {
         | 
| 102 | 
            -
                  _A.setNextRefresh();
         | 
| 103 | 
            -
                }
         | 
| 104 | 
            -
              });
         | 
| 105 | 
            -
            };
         | 
| 106 | 
            -
             | 
| 107 | 
            -
            _A.resultToCssClass = function(result) {
         | 
| 108 | 
            -
              var suffix = "ok"
         | 
| 109 | 
            -
              if (!result.daemon_running) {
         | 
| 110 | 
            -
                suffix = "daemon_stopped";
         | 
| 111 | 
            -
              } else if (!result.tasks_all_ok) {
         | 
| 112 | 
            -
                suffix = "task_failed";
         | 
| 113 | 
            -
              }
         | 
| 114 | 
            -
              return _A.CSS_CLASSES_PREFIX + suffix;
         | 
| 115 | 
            -
            };
         | 
| 7 | 
            +
            //= require tasks_scheduler/alert
         | 
| 8 | 
            +
            //= require tasks_scheduler/status
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            TasksScheduler.Alert = function () {
         | 
| 2 | 
            +
            };
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            _A = TasksScheduler.Alert;
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            _A.DEFAULT_REFRESH_INTERVAL = 5000;
         | 
| 7 | 
            +
            _A.DEFAULT_ELEMENT_SELECTOR = '#tasks_scheduler_alert';
         | 
| 8 | 
            +
            _A.CSS_CLASSES_PREFIX = 'alert_';
         | 
| 9 | 
            +
            _A.url = Routes.status_tasks_scheduler_daemon_path();
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            _A.init = function (options) {
         | 
| 12 | 
            +
              options = typeof options !== 'undefined' ? options : {};
         | 
| 13 | 
            +
              $(document).ready(function () {
         | 
| 14 | 
            +
                _A.options = options;
         | 
| 15 | 
            +
                if (!_A.options.refresh_interval) {
         | 
| 16 | 
            +
                  _A.options.refresh_interval = _A.DEFAULT_REFRESH_INTERVAL;
         | 
| 17 | 
            +
                }
         | 
| 18 | 
            +
                if (!_A.options.element_selector) {
         | 
| 19 | 
            +
                  _A.options.element_selector = _A.DEFAULT_ELEMENT_SELECTOR;
         | 
| 20 | 
            +
                }
         | 
| 21 | 
            +
                _A.refresh();
         | 
| 22 | 
            +
              });
         | 
| 23 | 
            +
            };
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            _A.setNextRefresh = function () {
         | 
| 26 | 
            +
              setTimeout(_A.refresh, _A.options.refresh_interval);
         | 
| 27 | 
            +
            };
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            _A.refresh = function () {
         | 
| 30 | 
            +
              $.ajax(_A.refreshAjaxData());
         | 
| 31 | 
            +
            };
         | 
| 32 | 
            +
             | 
| 33 | 
            +
            _A.refreshAjaxData = function () {
         | 
| 34 | 
            +
              return {
         | 
| 35 | 
            +
                url: _A.url,
         | 
| 36 | 
            +
                success: function (result) {
         | 
| 37 | 
            +
                  var alert = $(_A.options.element_selector);
         | 
| 38 | 
            +
                  var pattern = new RegExp('(^|\\s)' + _A.CSS_CLASSES_PREFIX + "\\S+", 'g');
         | 
| 39 | 
            +
                  alert.removeClass (function (index, className) {
         | 
| 40 | 
            +
                    return (className.match (pattern) || []).join(' ');
         | 
| 41 | 
            +
                  });
         | 
| 42 | 
            +
                  alert.addClass(_A.resultToCssClass(result));
         | 
| 43 | 
            +
                },
         | 
| 44 | 
            +
                complete: function (result) {
         | 
| 45 | 
            +
                  _A.setNextRefresh();
         | 
| 46 | 
            +
                }
         | 
| 47 | 
            +
              };
         | 
| 48 | 
            +
            };
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            _A.resultToCssClass = function(result) {
         | 
| 51 | 
            +
              var suffix = "ok"
         | 
| 52 | 
            +
              if (!result.daemon_running) {
         | 
| 53 | 
            +
                suffix = "daemon_stopped";
         | 
| 54 | 
            +
              } else if (!result.tasks_all_ok) {
         | 
| 55 | 
            +
                suffix = "task_failed";
         | 
| 56 | 
            +
              }
         | 
| 57 | 
            +
              return _A.CSS_CLASSES_PREFIX + suffix;
         | 
| 58 | 
            +
            };
         | 
| @@ -0,0 +1,58 @@ | |
| 1 | 
            +
            TasksScheduler.Status = function () {
         | 
| 2 | 
            +
            };
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            // Shortcut
         | 
| 5 | 
            +
            var _S = TasksScheduler.Status;
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            _S.initialized = false;
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            _S.init = function (url, interval_max) {
         | 
| 10 | 
            +
              if (!_S.initialized) {
         | 
| 11 | 
            +
                _S.initialized = true;
         | 
| 12 | 
            +
                _S.url = url;
         | 
| 13 | 
            +
                _S.interval_max = interval_max;
         | 
| 14 | 
            +
                _S.update();
         | 
| 15 | 
            +
              }
         | 
| 16 | 
            +
            };
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            _S.content = function () {
         | 
| 19 | 
            +
              return $('#TaskScheduler_Status_Content');
         | 
| 20 | 
            +
            };
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            _S.status = function () {
         | 
| 23 | 
            +
              return $('#TaskScheduler_Status_Status');
         | 
| 24 | 
            +
            };
         | 
| 25 | 
            +
             | 
| 26 | 
            +
            _S.update_status = function () {
         | 
| 27 | 
            +
              _S.status().html(
         | 
| 28 | 
            +
                "Updating in " + _S.interval + " seconds..."
         | 
| 29 | 
            +
              );
         | 
| 30 | 
            +
            };
         | 
| 31 | 
            +
             | 
| 32 | 
            +
            _S.check = function () {
         | 
| 33 | 
            +
              if (_S.interval <= 0) {
         | 
| 34 | 
            +
                _S.update();
         | 
| 35 | 
            +
              } else {
         | 
| 36 | 
            +
                _S.interval--;
         | 
| 37 | 
            +
                _S.update_status();
         | 
| 38 | 
            +
                setTimeout(_S.check, 1000);
         | 
| 39 | 
            +
              }
         | 
| 40 | 
            +
            };
         | 
| 41 | 
            +
             | 
| 42 | 
            +
            _S.update = function () {
         | 
| 43 | 
            +
              $.ajax(_S.updateAjaxData());
         | 
| 44 | 
            +
            };
         | 
| 45 | 
            +
             | 
| 46 | 
            +
            _S.updateAjaxData = function() {
         | 
| 47 | 
            +
              return {
         | 
| 48 | 
            +
                url: _S.url,
         | 
| 49 | 
            +
                success: function (result) {
         | 
| 50 | 
            +
                  _S.content().html(result);
         | 
| 51 | 
            +
                },
         | 
| 52 | 
            +
                complete: function (result) {
         | 
| 53 | 
            +
                  _S.interval = _S.interval_max + 1;
         | 
| 54 | 
            +
                  _S.last_update = new Date();
         | 
| 55 | 
            +
                  _S.check();
         | 
| 56 | 
            +
                }
         | 
| 57 | 
            +
              };
         | 
| 58 | 
            +
            };
         | 
| @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            class ScheduledTasksController < ApplicationController
         | 
| 2 4 | 
             
              class << self
         | 
| 3 5 | 
             
                private
         | 
| @@ -8,7 +10,7 @@ class ScheduledTasksController < ApplicationController | |
| 8 10 | 
             
              end
         | 
| 9 11 |  | 
| 10 12 | 
             
              active_scaffold :scheduled_task do |conf|
         | 
| 11 | 
            -
                [ | 
| 13 | 
            +
                %i[create update list].each do |action|
         | 
| 12 14 | 
             
                  conf.send(action).columns.exclude(:next_run, :last_fail_status, :last_run_start,
         | 
| 13 15 | 
             
                                                    :last_run_successful_start, :last_run_unsuccessful_start,
         | 
| 14 16 | 
             
                                                    :last_run_successful_end, :last_run_unsuccessful_end,
         | 
| @@ -26,8 +28,7 @@ class ScheduledTasksController < ApplicationController | |
| 26 28 | 
             
                @log_file = record.log_file(params[:identifier])
         | 
| 27 29 | 
             
              end
         | 
| 28 30 |  | 
| 29 | 
            -
              def status
         | 
| 30 | 
            -
              end
         | 
| 31 | 
            +
              def status; end
         | 
| 31 32 |  | 
| 32 33 | 
             
              def status_content
         | 
| 33 34 | 
             
                @scheduled_tasks = ::ScheduledTask.order(task: :asc, scheduling: :asc)
         | 
| @@ -36,7 +37,7 @@ class ScheduledTasksController < ApplicationController | |
| 36 37 |  | 
| 37 38 | 
             
              def run_now
         | 
| 38 39 | 
             
                process_action_link_action do |record|
         | 
| 39 | 
            -
                  record. | 
| 40 | 
            +
                  record.update!(next_run: Time.zone.now)
         | 
| 40 41 | 
             
                  record.reload
         | 
| 41 42 | 
             
                  flash[:info] = "Next run adjusted to #{record.next_run}"
         | 
| 42 43 | 
             
                end
         | 
| @@ -1,10 +1,11 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'tasks_scheduler/checker'
         | 
| 2 4 |  | 
| 3 5 | 
             
            class TasksSchedulerDaemonController < ApplicationController
         | 
| 4 6 | 
             
              require_relative 'tasks_scheduler_daemon_controller/_download_log'
         | 
| 5 7 |  | 
| 6 | 
            -
              def index
         | 
| 7 | 
            -
              end
         | 
| 8 | 
            +
              def index; end
         | 
| 8 9 |  | 
| 9 10 | 
             
              def execute
         | 
| 10 11 | 
             
                @result = ::TasksScheduler::Daemon.execute(params[:tasks_scheduler_execute_action])
         | 
| @@ -13,6 +14,6 @@ class TasksSchedulerDaemonController < ApplicationController | |
| 13 14 |  | 
| 14 15 | 
             
              def status
         | 
| 15 16 | 
             
                render json: { daemon_running: ::TasksScheduler::Daemon.running?,
         | 
| 16 | 
            -
                               tasks_all_ok:  | 
| 17 | 
            +
                               tasks_all_ok: ::ScheduledTask.all.none?(&:failed?) }
         | 
| 17 18 | 
             
              end
         | 
| 18 19 | 
             
            end
         | 
| @@ -1,8 +1,12 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            class TasksSchedulerDaemonController < ApplicationController
         | 
| 2 4 | 
             
              def download_log
         | 
| 3 5 | 
             
                return unless download_log_validate_log_key
         | 
| 6 | 
            +
             | 
| 4 7 | 
             
                log = ::TasksScheduler::Checker.instance.send("#{download_log_key}_log")
         | 
| 5 8 | 
             
                return unless download_log_validate_log_exist(log)
         | 
| 9 | 
            +
             | 
| 6 10 | 
             
                send_log_file(log)
         | 
| 7 11 | 
             
              end
         | 
| 8 12 |  | 
| @@ -14,6 +18,7 @@ class TasksSchedulerDaemonController < ApplicationController | |
| 14 18 |  | 
| 15 19 | 
             
              def download_log_validate_log_key
         | 
| 16 20 | 
             
                return true if ::TasksScheduler::Checker::LOGS_KEYS.include?(download_log_key)
         | 
| 21 | 
            +
             | 
| 17 22 | 
             
                redirect_to(tasks_scheduler_daemon_path,
         | 
| 18 23 | 
             
                            notice: "Invalid log key: \"#{download_log_key}\"")
         | 
| 19 24 | 
             
                false
         | 
| @@ -21,6 +26,7 @@ class TasksSchedulerDaemonController < ApplicationController | |
| 21 26 |  | 
| 22 27 | 
             
              def download_log_validate_log_exist(log)
         | 
| 23 28 | 
             
                return true if log.exist?
         | 
| 29 | 
            +
             | 
| 24 30 | 
             
                redirect_to(tasks_scheduler_daemon_path, notice: "Log \"#{log.key}\" does not exist.")
         | 
| 25 31 | 
             
                false
         | 
| 26 32 | 
             
              end
         | 
| @@ -1,3 +1,6 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require 'eac_ruby_utils/core_ext'
         | 
| 1 4 | 
             
            require 'rake'
         | 
| 2 5 |  | 
| 3 6 | 
             
            class ScheduledTask < ActiveRecord::Base
         | 
| @@ -6,7 +9,7 @@ class ScheduledTask < ActiveRecord::Base | |
| 6 9 | 
             
              include ::ScheduledTask::Runner
         | 
| 7 10 | 
             
              include ::ScheduledTask::Status
         | 
| 8 11 |  | 
| 9 | 
            -
              DEFAULT_TIMEOUT_ENVVAR_NAME = 'TASKS_SCHEDULER_TIMEOUT' | 
| 12 | 
            +
              DEFAULT_TIMEOUT_ENVVAR_NAME = 'TASKS_SCHEDULER_TIMEOUT'
         | 
| 10 13 | 
             
              DEFAULT_TIMEOUT = 12.hours
         | 
| 11 14 |  | 
| 12 15 | 
             
              class << self
         | 
| @@ -20,21 +23,18 @@ class ScheduledTask < ActiveRecord::Base | |
| 20 23 | 
             
                def timeout
         | 
| 21 24 | 
             
                  @timeout ||= begin
         | 
| 22 25 | 
             
                    r = Integer(ENV[DEFAULT_TIMEOUT_ENVVAR_NAME])
         | 
| 23 | 
            -
                    r  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            +
                    r.positive? ? r.seconds : DEFAULT_TIMEOUT
         | 
| 27 | 
            +
                               rescue ArgumentError, TypeError
         | 
| 28 | 
            +
                                 DEFAULT_TIMEOUT
         | 
| 26 29 | 
             
                  end
         | 
| 27 30 | 
             
                end
         | 
| 28 31 | 
             
              end
         | 
| 29 32 |  | 
| 30 | 
            -
               | 
| 31 | 
            -
               | 
| 32 | 
            -
             | 
| 33 | 
            -
              STATUS_ABORTED = 'aborted'
         | 
| 34 | 
            -
              STATUS_TIMEOUT = 'timeout'
         | 
| 35 | 
            -
              STATUS_DISABLED = 'disabled'
         | 
| 33 | 
            +
              enable_listable
         | 
| 34 | 
            +
              lists.add_string :status, 'aborted', 'disabled', 'failed', 'running', 'task_not_found', 'timeout',
         | 
| 35 | 
            +
                               'waiting'
         | 
| 36 36 |  | 
| 37 | 
            -
              LAST_FAIL_STATUSES = [STATUS_FAILED, STATUS_ABORTED, STATUS_TIMEOUT]
         | 
| 37 | 
            +
              LAST_FAIL_STATUSES = [STATUS_FAILED, STATUS_ABORTED, STATUS_TASK_NOT_FOUND, STATUS_TIMEOUT].freeze
         | 
| 38 38 |  | 
| 39 39 | 
             
              validates :scheduling, presence: true, 'tasks_scheduler/cron_scheduling': true
         | 
| 40 40 | 
             
              validates :task, presence: true
         | 
| @@ -69,13 +69,14 @@ class ScheduledTask < ActiveRecord::Base | |
| 69 69 |  | 
| 70 70 | 
             
              def process_running?
         | 
| 71 71 | 
             
                return false if pid.nil?
         | 
| 72 | 
            +
             | 
| 72 73 | 
             
                Process.kill(0, pid)
         | 
| 73 | 
            -
                 | 
| 74 | 
            +
                true
         | 
| 74 75 | 
             
              rescue Errno::EPERM
         | 
| 75 76 | 
             
                raise "No permission to query #{pid}!"
         | 
| 76 77 | 
             
              rescue Errno::ESRCH
         | 
| 77 | 
            -
                 | 
| 78 | 
            -
              rescue
         | 
| 78 | 
            +
                false
         | 
| 79 | 
            +
              rescue StandardError
         | 
| 79 80 | 
             
                raise "Unable to determine status for #{pid}"
         | 
| 80 81 | 
             
              end
         | 
| 81 82 |  | 
| @@ -87,6 +88,7 @@ class ScheduledTask < ActiveRecord::Base | |
| 87 88 | 
             
                return if task.blank?
         | 
| 88 89 | 
             
                return unless task_changed?
         | 
| 89 90 | 
             
                return if self.class.rake_tasks.include?(task)
         | 
| 91 | 
            +
             | 
| 90 92 | 
             
                errors.add(:task, "Task \"#{task}\" not found")
         | 
| 91 93 | 
             
              end
         | 
| 92 94 | 
             
            end
         | 
| @@ -1,4 +1,7 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'rake'
         | 
| 4 | 
            +
            require 'eac_ruby_utils/ruby'
         | 
| 2 5 |  | 
| 3 6 | 
             
            class ScheduledTask < ActiveRecord::Base
         | 
| 4 7 | 
             
              module Checker
         | 
| @@ -34,6 +37,7 @@ class ScheduledTask < ActiveRecord::Base | |
| 34 37 |  | 
| 35 38 | 
             
                def check_on_pid_not_present
         | 
| 36 39 | 
             
                  return unless enabled?
         | 
| 40 | 
            +
             | 
| 37 41 | 
             
                  if next_run.present?
         | 
| 38 42 | 
             
                    check_task_with_next_run
         | 
| 39 43 | 
             
                  else
         | 
| @@ -41,6 +45,12 @@ class ScheduledTask < ActiveRecord::Base | |
| 41 45 | 
             
                  end
         | 
| 42 46 | 
             
                end
         | 
| 43 47 |  | 
| 48 | 
            +
                def check_on_task_not_exist
         | 
| 49 | 
            +
                  message = "Task does not exist: #{task}"
         | 
| 50 | 
            +
                  check_log(message)
         | 
| 51 | 
            +
                  on_end_running(::StandardError.new(message), STATUS_TASK_NOT_FOUND)
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 44 54 | 
             
                def check_log(message, method = :info)
         | 
| 45 55 | 
             
                  Rails.logger.send(method, "TASK_CHECK(#{id}): #{message}")
         | 
| 46 56 | 
             
                end
         | 
| @@ -55,13 +65,13 @@ class ScheduledTask < ActiveRecord::Base | |
| 55 65 |  | 
| 56 66 | 
             
                def check_task_without_next_run
         | 
| 57 67 | 
             
                  check_log('Next run blank')
         | 
| 58 | 
            -
                   | 
| 68 | 
            +
                  update!(next_run: calculate_next_run)
         | 
| 59 69 | 
             
                  check_log("Next run scheduled: #{next_run.in_time_zone}")
         | 
| 60 70 | 
             
                end
         | 
| 61 71 |  | 
| 62 72 | 
             
                def check_task_with_next_run
         | 
| 63 73 | 
             
                  if !task_exist?
         | 
| 64 | 
            -
                     | 
| 74 | 
            +
                    check_on_task_not_exist
         | 
| 65 75 | 
             
                  elsif next_run < Time.zone.now
         | 
| 66 76 | 
             
                    check_log('Next run reached. Running...')
         | 
| 67 77 | 
             
                    spawn_task
         | 
| @@ -74,11 +84,13 @@ class ScheduledTask < ActiveRecord::Base | |
| 74 84 | 
             
                  params = ['bundle', 'exec', 'tasks_scheduler_run_task', id.to_s]
         | 
| 75 85 | 
             
                  check_log("Spawn command: #{params} (Task: #{task})")
         | 
| 76 86 | 
             
                  spawn_pid = nil
         | 
| 77 | 
            -
                   | 
| 78 | 
            -
                     | 
| 87 | 
            +
                  ::EacRubyUtils::Ruby.on_clean_environment do
         | 
| 88 | 
            +
                    Dir.chdir(Rails.root) do
         | 
| 89 | 
            +
                      spawn_pid = ::Process.spawn(*params)
         | 
| 90 | 
            +
                    end
         | 
| 79 91 | 
             
                  end
         | 
| 80 92 | 
             
                  Process.detach(spawn_pid)
         | 
| 81 | 
            -
                   | 
| 93 | 
            +
                  update!(pid: spawn_pid, last_fail_status: nil)
         | 
| 82 94 | 
             
                end
         | 
| 83 95 |  | 
| 84 96 | 
             
                def timeout?
         |