Skip to content

Add --exclude-group to cron:run #34117

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

20 changes: 18 additions & 2 deletions app/code/Magento/Cron/Console/Command/CronCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ class CronCommand extends Command
*/
const INPUT_KEY_GROUP = 'group';

/**
* Name of input option
*/
const INPUT_KEY_EXCLUDE_GROUP = 'exclude-group';

/**
* Object manager factory
*
Expand Down Expand Up @@ -59,7 +64,7 @@ public function __construct(
}

/**
* {@inheritdoc}
* Configures the current command.
*/
protected function configure()
{
Expand All @@ -70,6 +75,12 @@ protected function configure()
InputOption::VALUE_REQUIRED,
'Run jobs only from specified group'
),
new InputOption(
self::INPUT_KEY_EXCLUDE_GROUP,
null,
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Exclude jobs from the specified group'
),
new InputOption(
Cli::INPUT_KEY_BOOTSTRAP,
null,
Expand All @@ -86,20 +97,24 @@ protected function configure()
/**
* Runs cron jobs if cron is not disabled in Magento configurations
*
* {@inheritdoc}
* @param InputInterface $input
* @param OutputInterface $output
* @return int|void
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
if (!$this->deploymentConfig->get('cron/enabled', 1)) {
$output->writeln('<info>' . 'Cron is disabled. Jobs were not run.' . '</info>');
return;
}
// phpcs:ignore Magento2.Security.Superglobal.SuperglobalUsageWarning
$omParams = $_SERVER;
$omParams[StoreManager::PARAM_RUN_CODE] = 'admin';
$omParams[Store::CUSTOM_ENTRY_POINT_PARAM] = true;
$objectManager = $this->objectManagerFactory->create($omParams);

$params[self::INPUT_KEY_GROUP] = $input->getOption(self::INPUT_KEY_GROUP);
$params[self::INPUT_KEY_EXCLUDE_GROUP] = $input->getOption(self::INPUT_KEY_EXCLUDE_GROUP);
$params[ProcessCronQueueObserver::STANDALONE_PROCESS_STARTED] = '0';
$bootstrap = $input->getOption(Cli::INPUT_KEY_BOOTSTRAP);
if ($bootstrap) {
Expand All @@ -116,5 +131,6 @@ protected function execute(InputInterface $input, OutputInterface $output)
$cronObserver = $objectManager->create(\Magento\Framework\App\Cron::class, ['parameters' => $params]);
$cronObserver->launch();
$output->writeln('<info>' . 'Ran jobs by schedule.' . '</info>');
return Cli::RETURN_SUCCESS;
}
}
15 changes: 15 additions & 0 deletions app/code/Magento/Cron/Observer/ProcessCronQueueObserver.php
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,9 @@ function ($a, $b) {
if (!$this->isGroupInFilter($groupId)) {
continue;
}
if ($this->isGroupInExcludeFilter($groupId)) {
continue;
}
if ($this->_request->getParam(self::STANDALONE_PROCESS_STARTED) !== '1'
&& $this->getCronGroupConfigurationValue($groupId, 'use_separate_process') == 1
) {
Expand Down Expand Up @@ -795,6 +798,18 @@ private function isGroupInFilter($groupId): bool
&& trim($this->_request->getParam('group'), "'") !== $groupId);
}

/**
* Is Group In Exclude Filter.
*
* @param string $groupId
* @return bool
*/
private function isGroupInExcludeFilter($groupId): bool
{
$excludeGroup = $this->_request->getParam('exclude-group', []);
return is_array($excludeGroup) && in_array($groupId, $excludeGroup);
}

/**
* Process pending jobs.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
namespace Magento\Cron\Observer;

use Magento\Cron\Observer\ProcessCronQueueObserver;
use \Magento\TestFramework\Helper\Bootstrap;

class ProcessCronQueueObserverTest extends \PHPUnit\Framework\TestCase
Expand Down Expand Up @@ -49,4 +50,119 @@ public function testDispatchNoFailed()
$this->fail($item->getMessages());
}
}

/**
* @param array $expectedGroupsToRun
* @param null $group
* @param null $excludeGroup
* @dataProvider groupFiltersDataProvider
*/
public function testGroupFilters(array $expectedGroupsToRun, $group = null, $excludeGroup = null)
{
$config = $this->createMock(\Magento\Cron\Model\ConfigInterface::class);
$config->expects($this->any())
->method('getJobs')
->willReturn($this->getFilterTestCronGroups());

$request = Bootstrap::getObjectManager()->get(\Magento\Framework\App\Console\Request::class);
$lockManager = $this->createMock(\Magento\Framework\Lock\LockManagerInterface::class);

// The jobs are locked when they are run, assert on them to see which groups would run
$expectedLockData = [];
foreach ($expectedGroupsToRun as $expectedGroupToRun) {
$expectedLockData[] = [
ProcessCronQueueObserver::LOCK_PREFIX . $expectedGroupToRun,
ProcessCronQueueObserver::LOCK_TIMEOUT
];
}

// No expected lock data, means we should never call it
if (empty($expectedLockData)) {
$lockManager->expects($this->never())
->method('lock');
}

$lockManager->expects($this->exactly(count($expectedLockData)))
->method('lock')
->withConsecutive(...$expectedLockData);

$request->setParams(
[
'group' => $group,
'exclude-group' => $excludeGroup,
'standaloneProcessStarted' => '1'
]
);
$this->_model = Bootstrap::getObjectManager()
->create(\Magento\Cron\Observer\ProcessCronQueueObserver::class, [
'request' => $request,
'lockManager' => $lockManager,
'config' => $config
]);
$this->_model->execute(new \Magento\Framework\Event\Observer());
}

/**
* @return array|array[]
*/
public function groupFiltersDataProvider(): array
{

return [
'no flags runs all groups' => [
['consumers', 'index', 'default'] // groups to run
],
'--group=default should run' => [
['default'], // groups to run
'default', // --group default
],
'--group=default with --exclude-group=default, nothing should run' => [
[], // groups to run
'default', // --group default
['default'], // --exclude-group default
],
'--group=default with --exclude-group=index, default should run' => [
['default'], // groups to run
'default', // --group default
['index'], // --exclude-group index
],
'--group=index with --exclude-group=default, index should run' => [
['index'], // groups to run
'index', // --group index
['default'], // --exclude-group default
],
'--exclude-group=index, all other groups should run' => [
['consumers', 'default'], // groups to run, all but index
null, //
['index'] // --exclude-group index
],
'--exclude-group for every group runs nothing' => [
[], // groups to run, none
null, //
['default', 'consumers', 'index'] // groups to exclude, all of them
],
'exclude all groups but consumers, consumers runs' => [
['consumers'],
null,
['index', 'default']
],
];
}

/**
* Only run the filter group tests with a limited set of cron groups, keeps tests consistent between EE and CE
*
* @return array
*/
private function getFilterTestCronGroups()
{
$listOfGroups = [];
$config = Bootstrap::getObjectManager()->get(\Magento\Cron\Model\ConfigInterface::class);
foreach ($config->getJobs() as $groupId => $data) {
if (in_array($groupId, ['default', 'consumers', 'index'])) {
$listOfGroups[$groupId] = $data;
}
}
return $listOfGroups;
}
}