From d7342dc23972b430b5995f6d0fe6fb878ef5a65a Mon Sep 17 00:00:00 2001 From: Mikhail Golenkov Date: Tue, 25 Aug 2020 16:49:20 +1000 Subject: [PATCH] MDL-67211 Tasks: Add cron_enabled setting. Co-authored-by: Sam Marshall --- admin/cli/adhoc_task.php | 9 ++ admin/cli/cron.php | 111 +++++++++++++++++- admin/cli/scheduled_task.php | 19 ++- admin/settings/server.php | 10 ++ admin/tool/task/lang/en/tool_task.php | 1 + admin/tool/task/renderer.php | 10 ++ admin/tool/task/runningtasks.php | 5 + admin/tool/task/scheduledtasks.php | 3 + .../task/tests/behat/cron_disabled.feature | 20 ++++ lang/en/admin.php | 2 + lib/classes/task/manager.php | 2 +- lib/outputlib.php | 10 ++ 12 files changed, 193 insertions(+), 9 deletions(-) create mode 100644 admin/tool/task/tests/behat/cron_disabled.feature diff --git a/admin/cli/adhoc_task.php b/admin/cli/adhoc_task.php index b0ed21d376880..dd1cc14b478b4 100644 --- a/admin/cli/adhoc_task.php +++ b/admin/cli/adhoc_task.php @@ -37,11 +37,13 @@ 'showsql' => false, 'showdebugging' => false, 'ignorelimits' => false, + 'force' => false, ], [ 'h' => 'help', 'e' => 'execute', 'k' => 'keep-alive', 'i' => 'ignorelimits', + 'f' => 'force', ] ); @@ -61,6 +63,7 @@ -e, --execute Run all queued adhoc tasks -k, --keep-alive=N Keep this script alive for N seconds and poll for new adhoc tasks -i --ignorelimits Ignore task_adhoc_concurrency_limit and task_adhoc_max_runtime limits + -f, --force Run even if cron is disabled Example: \$sudo -u www-data /usr/bin/php admin/cli/adhoc_task.php --execute @@ -92,6 +95,12 @@ if (empty($options['execute'])) { exit(0); } + +if (!get_config('core', 'cron_enabled') && !$options['force']) { + mtrace('Cron is disabled. Use --force to override.'); + exit(1); +} + if (empty($options['keep-alive'])) { $options['keep-alive'] = 0; } diff --git a/admin/cli/cron.php b/admin/cli/cron.php index fe726830117cb..958062a5143f3 100644 --- a/admin/cli/cron.php +++ b/admin/cli/cron.php @@ -36,14 +36,23 @@ // now get cli options list($options, $unrecognized) = cli_get_params( - array( + [ 'help' => false, 'stop' => false, - ), - array( + 'list' => false, + 'force' => false, + 'enable' => false, + 'disable' => false, + 'disable-wait' => false, + ], [ 'h' => 'help', 's' => 'stop', - ) + 'l' => 'list', + 'f' => 'force', + 'e' => 'enable', + 'd' => 'disable', + 'w' => 'disable-wait', + ] ); if ($unrecognized) { @@ -56,8 +65,13 @@ "Execute periodic cron actions. Options: --h, --help Print out this help --s, --stop Notify all other running cron processes to stop after the current task +-h, --help Print out this help +-s, --stop Notify all other running cron processes to stop after the current task +-l, --list Show the list of currently running tasks and how long they have been running +-f, --force Execute task even if cron is disabled +-e, --enable Enable cron +-d, --disable Disable cron +-w, --disable-wait=600 Disable cron and wait until all tasks finished or fail after N seconds (optional param) Example: \$sudo -u www-data /usr/bin/php admin/cli/cron.php @@ -74,6 +88,91 @@ die; } +if ($options['enable']) { + set_config('cron_enabled', 1); + mtrace('Cron has been enabled for the site.'); + exit(0); +} + +if ($options['disable']) { + set_config('cron_enabled', 0); + \core\task\manager::clear_static_caches(); + mtrace('Cron has been disabled for the site.'); + exit(0); +} + +if ($options['list']) { + $tasks = \core\task\manager::get_running_tasks(); + mtrace('The list of currently running tasks:'); + $format = "%7s %-12s %-9s %-20s %-52s\n"; + printf ($format, + 'PID', + 'HOST', + 'TYPE', + 'TIME', + 'CLASSNAME' + ); + foreach ($tasks as $task) { + printf ($format, + $task->pid, + substr($task->hostname, 0, 12), + $task->type, + format_time(time() - $task->timestarted), + substr($task->classname, 0, 52) + ); + } + exit(0); +} + +if ($wait = $options['disable-wait']) { + $started = time(); + if (true === $wait) { + // Default waiting time. + $waitsec = 600; + } else { + $waitsec = $wait; + $wait = true; + } + + set_config('cron_enabled', 0); + \core\task\manager::clear_static_caches(); + mtrace('Cron has been disabled for the site.'); + mtrace('Allocating '. format_time($waitsec) . ' for the tasks to finish.'); + + $lastcount = 0; + while ($wait) { + $tasks = \core\task\manager::get_running_tasks(); + + if (count($tasks) == 0) { + mtrace(''); + mtrace('All scheduled and adhoc tasks finished.'); + exit(0); + } + + if (time() - $started >= $waitsec) { + mtrace(''); + mtrace('Wait time ('. format_time($waitsec) . ') elapsed, but ' . count($tasks) . ' task(s) still running.'); + mtrace('Exiting with code 1.'); + exit(1); + } + + if (count($tasks) !== $lastcount) { + mtrace(''); + mtrace(count($tasks) . " tasks currently running.", ''); + $lastcount = count($tasks); + } else { + mtrace('.', ''); + } + + sleep(1); + } +} + +if (!get_config('core', 'cron_enabled') && !$options['force']) { + mtrace('Cron is disabled. Use --force to override.'); + exit(1); +} + \core\local\cli\shutdown::script_supports_graceful_exit(); cron_run(); diff --git a/admin/cli/scheduled_task.php b/admin/cli/scheduled_task.php index bd3ad0ed7b50e..b181b1549bf41 100644 --- a/admin/cli/scheduled_task.php +++ b/admin/cli/scheduled_task.php @@ -30,8 +30,17 @@ require_once("$CFG->libdir/cronlib.php"); list($options, $unrecognized) = cli_get_params( - array('help' => false, 'list' => false, 'execute' => false, 'showsql' => false, 'showdebugging' => false), - array('h' => 'help') + [ + 'help' => false, + 'list' => false, + 'execute' => false, + 'showsql' => false, + 'showdebugging' => false, + 'force' => false, + ], [ + 'h' => 'help', + 'f' => 'force', + ] ); if ($unrecognized) { @@ -49,6 +58,7 @@ --showsql Show sql queries before they are executed --showdebugging Show developer level debugging information -h, --help Print out this help + -f, --force Execute task even if cron is disabled Example: \$sudo -u www-data /usr/bin/php admin/cli/scheduled_task.php --execute=\\core\\task\\session_cleanup_task @@ -121,6 +131,11 @@ exit(1); } + if (!get_config('core', 'cron_enabled') && !$options['force']) { + mtrace('Cron is disabled. Use --force to override.'); + exit(1); + } + \core\task\manager::scheduled_task_starting($task); // Increase memory limit. diff --git a/admin/settings/server.php b/admin/settings/server.php index e86bbc951014c..e9fac46fc8b18 100644 --- a/admin/settings/server.php +++ b/admin/settings/server.php @@ -216,6 +216,16 @@ $ADMIN->add('server', new admin_category('taskconfig', new lang_string('taskadmintitle', 'admin'))); $temp = new admin_settingpage('taskprocessing', new lang_string('taskprocessing','admin')); + +$setting = new admin_setting_configcheckbox( + 'cron_enabled', + new lang_string('cron_enabled', 'admin'), + new lang_string('cron_enabled_desc', 'admin'), + 1 +); +$setting->set_updatedcallback('theme_reset_static_caches'); +$temp->add($setting); + $temp->add( new admin_setting_configtext( 'task_scheduled_concurrency_limit', diff --git a/admin/tool/task/lang/en/tool_task.php b/admin/tool/task/lang/en/tool_task.php index be26065792852..08cf819408625 100644 --- a/admin/tool/task/lang/en/tool_task.php +++ b/admin/tool/task/lang/en/tool_task.php @@ -39,6 +39,7 @@ $string['clearfaildelay_confirm'] = 'Are you sure you want to clear the fail delay for task \'{$a}\'? After clearing the delay, the task will run according to its normal schedule.'; $string['component'] = 'Component'; $string['corecomponent'] = 'Core'; +$string['crondisabled'] = 'Cron is disabled. No new tasks will be started. The system will not operate properly until it is enabled again.'; $string['cronok'] = 'Cron is running frequently'; $string['default'] = 'Default'; $string['defaultx'] = 'Default: {$a}'; diff --git a/admin/tool/task/renderer.php b/admin/tool/task/renderer.php index ec0bb0cac4a3f..ec85231c5a32e 100644 --- a/admin/tool/task/renderer.php +++ b/admin/tool/task/renderer.php @@ -261,6 +261,16 @@ protected function time_cell(string $current, string $default): html_table_cell return $cell; } + /** + * Displays a warning on the page if cron is disabled. + * + * @return string HTML code for information about cron being disabled + * @throws moodle_exception + */ + public function cron_disabled(): string { + return $this->output->notification(get_string('crondisabled', 'tool_task'), 'warning'); + } + /** * Renders a link back to the scheduled tasks page (used from the 'run now' screen). * diff --git a/admin/tool/task/runningtasks.php b/admin/tool/task/runningtasks.php index d5c284274410e..05829a31112fe 100644 --- a/admin/tool/task/runningtasks.php +++ b/admin/tool/task/runningtasks.php @@ -39,6 +39,11 @@ echo $OUTPUT->header(); +if (!get_config('core', 'cron_enabled')) { + $renderer = $PAGE->get_renderer('tool_task'); + echo $renderer->cron_disabled(); +} + $table = new \tool_task\running_tasks_table(); $table->baseurl = $pageurl; $table->out(100, false); diff --git a/admin/tool/task/scheduledtasks.php b/admin/tool/task/scheduledtasks.php index d256f3d7175b8..243039e6b6d73 100644 --- a/admin/tool/task/scheduledtasks.php +++ b/admin/tool/task/scheduledtasks.php @@ -95,6 +95,9 @@ } else { echo $OUTPUT->header(); + if (!get_config('core', 'cron_enabled')) { + echo $renderer->cron_disabled(); + } $tasks = core\task\manager::get_all_scheduled_tasks(); echo $renderer->scheduled_tasks_table($tasks, $lastchanged); echo $OUTPUT->footer(); diff --git a/admin/tool/task/tests/behat/cron_disabled.feature b/admin/tool/task/tests/behat/cron_disabled.feature new file mode 100644 index 0000000000000..24c7e87ac4b1b --- /dev/null +++ b/admin/tool/task/tests/behat/cron_disabled.feature @@ -0,0 +1,20 @@ +@tool @tool_task +Feature: See warning message if cron is disabled + In order to manage scheduled tasks + As a Moodle Administrator + I need to be able to view a warning message if cron is disabled + + Background: + Given I log in as "admin" + + Scenario: If cron is disabled, I should see the message + When the following config values are set as admin: + | cron_enabled | 0 | + And I navigate to "Server > Tasks > Scheduled tasks" in site administration + Then I should see "Cron is disabled" + + Scenario: If cron is enabled, I should not see the message + When the following config values are set as admin: + | cron_enabled | 1 | + And I navigate to "Server > Tasks > Scheduled tasks" in site administration + Then I should not see "Cron is disabled" diff --git a/lang/en/admin.php b/lang/en/admin.php index 5cb42af68cc57..2066a80fc52e9 100644 --- a/lang/en/admin.php +++ b/lang/en/admin.php @@ -424,6 +424,8 @@ $string['creatornewroleid'] = 'Creators\' role in new courses'; $string['creatornewroleid_help'] = 'If the user does not already have the permission to manage the new course, the user is automatically enrolled using this role.'; $string['cron'] = 'Cron'; +$string['cron_enabled'] = 'Enable cron'; +$string['cron_enabled_desc'] = 'If disabled prevents the system from starting new background tasks. This option is intended for temporary use only, e.g. before a restart. Leaving it off for a long time will prevent important functionality from working.'; $string['cron_help'] = 'The cron.php script runs a number of tasks at different scheduled intervals, such as sending forum post notification emails. The script should be run regularly - ideally every minute.'; $string['cron_link'] = 'admin/cron'; $string['cronclionly'] = 'Cron execution via command line only'; diff --git a/lib/classes/task/manager.php b/lib/classes/task/manager.php index aebdc44335857..08b624222ac06 100644 --- a/lib/classes/task/manager.php +++ b/lib/classes/task/manager.php @@ -1095,7 +1095,7 @@ public static function run_from_cli(\core\task\task_base $task):bool { // Shell-escaped task name. $classname = get_class($task); - $taskarg = escapeshellarg("--execute={$classname}"); + $taskarg = escapeshellarg("--execute={$classname}") . " " . escapeshellarg("--force"); // Build the CLI command. $command = "{$phpbinary} {$scriptpath} {$taskarg}"; diff --git a/lib/outputlib.php b/lib/outputlib.php index fb4a29c98f9bf..f324c4b2afb5b 100644 --- a/lib/outputlib.php +++ b/lib/outputlib.php @@ -270,6 +270,16 @@ function theme_reset_all_caches() { } } +/** + * Reset static caches. + * + * This method indicates that all running cron processes should exit at the + * next opportunity. + */ +function theme_reset_static_caches() { + \core\task\manager::clear_static_caches(); +} + /** * Enable or disable theme designer mode. *