Skip to content

Commit e99f782

Browse files
oshmyheliukshiftedreality
authored andcommitted
MAGECLOUD-1355: Cron Reset CLI Command for 2.2.x (#108)
1 parent 1830a72 commit e99f782

File tree

5 files changed

+227
-39
lines changed

5 files changed

+227
-39
lines changed

src/App/Container.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
namespace Magento\MagentoCloud\App;
77

88
use Magento\MagentoCloud\Command\Build;
9+
use Magento\MagentoCloud\Command\CronUnlock;
910
use Magento\MagentoCloud\Command\Deploy;
1011
use Magento\MagentoCloud\Command\ConfigDump;
1112
use Magento\MagentoCloud\Process\ProcessInterface;
@@ -148,6 +149,15 @@ public function __construct(string $root, array $config)
148149
],
149150
]);
150151
});
152+
$this->when(CronUnlock::class)
153+
->needs(ProcessInterface::class)
154+
->give(function () {
155+
return $this->makeWith(ProcessPool::class, [
156+
'processes' => [
157+
$this->make(DeployProcess\UnlockCronJobs::class),
158+
],
159+
]);
160+
});
151161
$this->when(DeployProcess\PreDeploy::class)
152162
->needs(ProcessInterface::class)
153163
->give(function () {

src/Application.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
use Composer\Composer;
99
use Magento\MagentoCloud\Command\Build;
10+
use Magento\MagentoCloud\Command\CronUnlock;
1011
use Magento\MagentoCloud\Command\Deploy;
1112
use Magento\MagentoCloud\Command\ConfigDump;
1213
use Psr\Container\ContainerInterface;
@@ -45,6 +46,7 @@ protected function getDefaultCommands()
4546
$this->container->get(Build::class),
4647
$this->container->get(Deploy::class),
4748
$this->container->get(ConfigDump::class),
49+
$this->container->get(CronUnlock::class),
4850
]
4951
);
5052
}

src/Command/CronUnlock.php

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\MagentoCloud\Command;
7+
8+
use Magento\MagentoCloud\Process\ProcessInterface;
9+
use Psr\Log\LoggerInterface;
10+
use Symfony\Component\Console\Command\Command;
11+
use Symfony\Component\Console\Input\InputInterface;
12+
use Symfony\Component\Console\Output\OutputInterface;
13+
14+
/**
15+
* CLI command for unlocking cron jobs that stuck in "running" state.
16+
*/
17+
class CronUnlock extends Command
18+
{
19+
const NAME = 'cron:unlock';
20+
21+
/**
22+
* @var ProcessInterface
23+
*/
24+
private $process;
25+
26+
/**
27+
* @var LoggerInterface
28+
*/
29+
private $logger;
30+
31+
/**
32+
* @param ProcessInterface $process
33+
* @param LoggerInterface $logger
34+
*/
35+
public function __construct(ProcessInterface $process, LoggerInterface $logger)
36+
{
37+
$this->process = $process;
38+
$this->logger = $logger;
39+
40+
parent::__construct();
41+
}
42+
43+
/**
44+
* @inheritdoc
45+
*/
46+
protected function configure()
47+
{
48+
$this->setName(static::NAME)
49+
->setDescription('Unlock cron jobs that stuck in "running" state.');
50+
51+
parent::configure();
52+
}
53+
54+
/**
55+
* @inheritdoc
56+
*/
57+
public function execute(InputInterface $input, OutputInterface $output)
58+
{
59+
try {
60+
$this->logger->info('Starting unlocking.');
61+
$this->process->execute();
62+
$this->logger->info('Unlocking completed.');
63+
} catch (\Exception $exception) {
64+
$this->logger->critical($exception->getMessage());
65+
66+
throw $exception;
67+
}
68+
}
69+
}

src/Test/Unit/ApplicationTest.php

Lines changed: 60 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
use Magento\MagentoCloud\Application;
1111
use Magento\MagentoCloud\Command\Build;
1212
use Magento\MagentoCloud\Command\ConfigDump;
13+
use Magento\MagentoCloud\Command\CronUnlock;
1314
use Magento\MagentoCloud\Command\Deploy;
1415
use PHPUnit\Framework\TestCase;
1516
use Psr\Container\ContainerInterface;
@@ -37,19 +38,14 @@ class ApplicationTest extends TestCase
3738
private $packageMock;
3839

3940
/**
40-
* @var Build|\PHPUnit_Framework_MockObject_MockObject
41+
* @var string
4142
*/
42-
private $buildCommandMock;
43+
private $applicationVersion = '1.0';
4344

4445
/**
45-
* @var Deploy|\PHPUnit_Framework_MockObject_MockObject
46+
* @var string
4647
*/
47-
private $deployCommandMock;
48-
49-
/**
50-
* @var ConfigDump|\PHPUnit_Framework_MockObject_MockObject
51-
*/
52-
private $configDumpCommand;
48+
private $applicationName = 'Magento Cloud Tools';
5349

5450
/**
5551
* @inheritdoc
@@ -59,54 +55,79 @@ public function setUp()
5955
$this->containerMock = $this->getMockForAbstractClass(ContainerInterface::class);
6056
$this->packageMock = $this->getMockForAbstractClass(PackageInterface::class);
6157
$this->composerMock = $this->createMock(Composer::class);
62-
$this->buildCommandMock = $this->createMock(Build::class);
63-
$this->deployCommandMock = $this->createMock(Deploy::class);
64-
$this->configDumpCommand = $this->createMock(Deploy::class);
6558

66-
$this->buildCommandMock->method('getName')
67-
->willReturn(Build::NAME);
68-
$this->buildCommandMock->method('isEnabled')
69-
->willReturn(true);
70-
$this->buildCommandMock->method('getDefinition')
71-
->willReturn([]);
72-
$this->buildCommandMock->method('getAliases')
73-
->willReturn([]);
74-
$this->deployCommandMock->method('getName')
75-
->willReturn(Deploy::NAME);
76-
$this->deployCommandMock->method('isEnabled')
77-
->willReturn(true);
78-
$this->deployCommandMock->method('getDefinition')
79-
->willReturn([]);
80-
$this->deployCommandMock->method('getAliases')
81-
->willReturn([]);
82-
$this->configDumpCommand->method('getName')
83-
->willReturn(ConfigDump::NAME);
84-
$this->configDumpCommand->method('isEnabled')
85-
->willReturn(true);
86-
$this->configDumpCommand->method('getDefinition')
87-
->willReturn([]);
88-
$this->configDumpCommand->method('getAliases')
89-
->willReturn([]);
59+
/**
60+
* Command mocks.
61+
*/
62+
$buildCommandMock = $this->createMock(Build::class);
63+
$deployCommandMock = $this->createMock(Deploy::class);
64+
$configDumpCommand = $this->createMock(ConfigDump::class);
65+
$cronUnlockCommand = $this->createMock(CronUnlock::class);
66+
67+
$this->mockCommand($buildCommandMock, Build::NAME);
68+
$this->mockCommand($deployCommandMock, Deploy::NAME);
69+
$this->mockCommand($configDumpCommand, ConfigDump::NAME);
70+
$this->mockCommand($cronUnlockCommand, CronUnlock::NAME);
9071

9172
$this->containerMock->method('get')
9273
->willReturnMap([
9374
[Composer::class, $this->composerMock],
94-
[Build::class, $this->buildCommandMock],
95-
[Deploy::class, $this->deployCommandMock],
96-
[ConfigDump::class, $this->configDumpCommand],
75+
[Build::class, $buildCommandMock],
76+
[Deploy::class, $deployCommandMock],
77+
[ConfigDump::class, $configDumpCommand],
78+
[CronUnlock::class, $cronUnlockCommand],
9779
]);
9880
$this->composerMock->method('getPackage')
9981
->willReturn($this->packageMock);
82+
$this->packageMock->expects($this->once())
83+
->method('getPrettyName')
84+
->willReturn($this->applicationName);
85+
$this->packageMock->expects($this->once())
86+
->method('getPrettyVersion')
87+
->willReturn($this->applicationVersion);
10088

10189
$this->application = new Application(
10290
$this->containerMock
10391
);
10492
}
10593

94+
/**
95+
* @param \PHPUnit_Framework_MockObject_MockObject $command
96+
* @param string $name
97+
*/
98+
private function mockCommand(\PHPUnit_Framework_MockObject_MockObject $command, string $name)
99+
{
100+
$command->method('getName')
101+
->willReturn($name);
102+
$command->method('isEnabled')
103+
->willReturn(true);
104+
$command->method('getDefinition')
105+
->willReturn([]);
106+
$command->method('getAliases')
107+
->willReturn([]);
108+
}
109+
106110
public function testHasCommand()
107111
{
108112
$this->assertTrue($this->application->has(Build::NAME));
109113
$this->assertTrue($this->application->has(Deploy::NAME));
110114
$this->assertTrue($this->application->has(ConfigDump::NAME));
115+
$this->assertTrue($this->application->has(CronUnlock::NAME));
116+
}
117+
118+
public function testGetName()
119+
{
120+
$this->assertSame(
121+
$this->applicationName,
122+
$this->application->getName()
123+
);
124+
}
125+
126+
public function testGetVersion()
127+
{
128+
$this->assertSame(
129+
$this->applicationVersion,
130+
$this->application->getVersion()
131+
);
111132
}
112133
}
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\MagentoCloud\Test\Unit\Command;
7+
8+
use Magento\MagentoCloud\Command\CronUnlock;
9+
use Magento\MagentoCloud\Process\ProcessInterface;
10+
use PHPUnit\Framework\TestCase;
11+
use Psr\Log\LoggerInterface;
12+
use Symfony\Component\Console\Tester\CommandTester;
13+
use PHPUnit_Framework_MockObject_MockObject as Mock;
14+
15+
class CronUnlockTest extends TestCase
16+
{
17+
/**
18+
* @var ProcessInterface|Mock
19+
*/
20+
private $processMock;
21+
22+
/**
23+
* @var LoggerInterface|Mock
24+
*/
25+
private $loggerMock;
26+
27+
/**
28+
* @var CronUnlock
29+
*/
30+
private $cronUnlockCommand;
31+
32+
/**
33+
* @inheritdoc
34+
*/
35+
protected function setUp()
36+
{
37+
$this->processMock = $this->getMockForAbstractClass(ProcessInterface::class);
38+
$this->loggerMock = $this->getMockForAbstractClass(LoggerInterface::class);
39+
40+
$this->cronUnlockCommand = new CronUnlock(
41+
$this->processMock,
42+
$this->loggerMock
43+
);
44+
}
45+
46+
public function testExecute()
47+
{
48+
$this->loggerMock->expects($this->exactly(2))
49+
->method('info')
50+
->withConsecutive(
51+
['Starting unlocking.'],
52+
['Unlocking completed.']
53+
);
54+
$this->processMock->expects($this->once())
55+
->method('execute');
56+
57+
$tester = new CommandTester(
58+
$this->cronUnlockCommand
59+
);
60+
$tester->execute([]);
61+
62+
$this->assertSame(0, $tester->getStatusCode());
63+
}
64+
65+
/**
66+
* @expectedException \Exception
67+
* @expectedExceptionMessage Some error
68+
*/
69+
public function testExecuteWithException()
70+
{
71+
$this->loggerMock->expects($this->once())
72+
->method('info')
73+
->with('Starting unlocking.');
74+
$this->loggerMock->expects($this->once())
75+
->method('critical')
76+
->with('Some error');
77+
$this->processMock->expects($this->once())
78+
->method('execute')
79+
->willThrowException(new \Exception('Some error'));
80+
81+
$tester = new CommandTester(
82+
$this->cronUnlockCommand
83+
);
84+
$tester->execute([]);
85+
}
86+
}

0 commit comments

Comments
 (0)