Skip to content
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

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

14 changes: 14 additions & 0 deletions app/code/Magento/Cron/Console/Command/CronCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ class CronCommand extends Command
public const INPUT_KEY_GROUP = 'group';

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

/**
*
* @var ObjectManagerFactory
*/
private $objectManagerFactory;
Expand Down Expand Up @@ -73,6 +79,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 Down Expand Up @@ -102,13 +114,15 @@ protected function execute(InputInterface $input, OutputInterface $output)
$output->writeln('<info>' . 'Cron is disabled. Jobs were not run.' . '</info>');
return Cli::RETURN_SUCCESS;
}

// phpcs:ignore Magento2.Security.Superglobal
$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 Down
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 @@ -257,6 +257,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 @@ -809,6 +812,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' => [
['index', 'consumers', '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;
}
}