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

feat: Use health checks for monitor command #258

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Command/DevRobotsTxtCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class DevRobotsTxtCommand extends Command
{
public function __construct(
#[Autowire('%kernel.project_dir%/public')]
private readonly string $envPath
private readonly string $envPath,
) {
parent::__construct();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/EnvDelCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class EnvDelCommand extends Command
{
public function __construct(
#[Autowire('%kernel.project_dir%/.env')]
private readonly string $envPath
private readonly string $envPath,
) {
parent::__construct();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/EnvGetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class EnvGetCommand extends Command
{
public function __construct(
#[Autowire('%kernel.project_dir%/.env')]
private readonly string $envPath
private readonly string $envPath,
) {
parent::__construct();
}
Expand Down
2 changes: 1 addition & 1 deletion src/Command/EnvSetCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class EnvSetCommand extends Command
{
public function __construct(
#[Autowire('%kernel.project_dir%/.env')]
private readonly string $envPath
private readonly string $envPath,
) {
parent::__construct();
}
Expand Down
94 changes: 31 additions & 63 deletions src/Command/MonitorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,13 @@

namespace Frosh\Tools\Command;

use Doctrine\DBAL\Connection;
use Frosh\Tools\Components\Health\Checker\HealthChecker\QueueChecker;
use Frosh\Tools\Components\Health\Checker\HealthChecker\TaskChecker;
use Frosh\Tools\Components\Health\HealthCollection;
use Frosh\Tools\Components\Health\SettingsResult;
use Shopware\Core\Content\Mail\Service\AbstractMailService;
use Shopware\Core\Content\Mail\Service\MailService;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepository;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\NotFilter;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\RangeFilter;
use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskCollection;
use Shopware\Core\Framework\MessageQueue\ScheduledTask\ScheduledTaskDefinition;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
Expand All @@ -25,28 +21,25 @@
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\ParameterBag;

#[AsCommand('frosh:monitor', 'Monitor your scheduled tasks and queue with this command and get notified via email.')]
#[AsCommand('frosh:monitor', 'Monitor your scheduled tasks and message queue and get notified via email.')]
class MonitorCommand extends Command
{
private const MONITOR_EMAIL_OPTION = 'email';
private const MONITOR_SALESCHANNEL_ARG = 'sales-channel';

/**
* @param EntityRepository<ScheduledTaskCollection> $scheduledTaskRepository
*/
public function __construct(
#[Autowire(service: MailService::class)]
private readonly AbstractMailService $mailService,
private readonly SystemConfigService $configService,
private readonly Connection $connection,
private readonly EntityRepository $scheduledTaskRepository
private readonly QueueChecker $queueChecker,
private readonly TaskChecker $taskChecker,
) {
parent::__construct();
}

protected function configure(): void
{
$this->addArgument('sales-channel', InputArgument::REQUIRED, 'Sales Channel ID.');
$this->addArgument(self::MONITOR_SALESCHANNEL_ARG, InputArgument::REQUIRED, 'Sales Channel ID.');
$this->addOption(self::MONITOR_EMAIL_OPTION, 'em', InputOption::VALUE_OPTIONAL, 'Custom mail address');
}

Expand All @@ -55,30 +48,30 @@ protected function execute(InputInterface $input, OutputInterface $output): int
$context = Context::createDefaultContext();

if ($input->getOption(self::MONITOR_EMAIL_OPTION)) {
$recepientMail = $input->getOption(self::MONITOR_EMAIL_OPTION);
$recipientMail = $input->getOption(self::MONITOR_EMAIL_OPTION);
$errorSource = 'CLI option';
} else {
$recepientMail = $this->configService->getString(
'FroshTools.config.monitorMail'
$recipientMail = $this->configService->getString(
'FroshTools.config.monitorMail',
);
$errorSource = 'plugin config';
}

if (!filter_var($recepientMail, \FILTER_VALIDATE_EMAIL)) {
$output->writeln('<error>Invalid email format in ' . $errorSource . '</error>');
if (empty($recipientMail) || !filter_var($recipientMail, \FILTER_VALIDATE_EMAIL)) {
$output->writeln('<error>Empty or invalid email format in ' . $errorSource . '</error>');

return self::INVALID;
}

if (!empty($recepientMail) && ($this->queueFailed() || $this->scheduledTaskFailed())) {
if ($this->checksFailed()) {
$data = new ParameterBag();
$data->set(
'recipients',
[
$recepientMail => 'Admin',
]
$recipientMail => 'Admin',
],
);
$data->set('senderName', 'Froshtools | Admin');
$data->set('senderName', 'FroshTools | Admin');

$htmlMailContent = <<<'MAIL'
<div>
Expand All @@ -92,57 +85,32 @@ protected function execute(InputInterface $input, OutputInterface $output): int
</p>
</div>
MAIL;
$plainMailContent = 'Dear Admin,your message queue or scheduled tasks are not working as expected.Check your queues and tasks {{ salesChannel.domains|first.url }}/admin#/frosh/tools/index/index';
$plainMailContent = 'Dear Admin, your message queue or scheduled tasks are not working as expected. Check your queues and tasks {{ salesChannel.domains|first.url }}/admin#/frosh/tools/index/index';

$data->set('contentHtml', $htmlMailContent);
$data->set('contentPlain', $plainMailContent);
$data->set('salesChannelId', $input->getArgument(self::MONITOR_SALESCHANNEL_ARG));
$data->set('subject', 'Froshtools message queue and scheduled task | Warning');
$data->set('subject', 'FroshTools message queue and scheduled task | Warning');

$this->mailService->send($data->all(), $context);
}

return self::SUCCESS;
}

private function queueFailed(): bool
private function checksFailed(): bool
{
/** @var string $availableAt */
$availableAt = $this->connection->fetchOne('SELECT IFNULL(MIN(available_at), 0) FROM messenger_messages');
$oldestMessage = (int) strtotime($availableAt);
$minutes = $this->configService->getInt(
'FroshTools.config.monitorQueueGraceTime'
);

return $oldestMessage && ($oldestMessage + ($minutes * 60)) < time();
}
$collection = new HealthCollection();
$this->queueChecker->collect($collection);
$this->taskChecker->collect($collection);

/** @var SettingsResult $result */
foreach ($collection as $result) {
if ($result->state !== SettingsResult::GREEN && $result->state !== SettingsResult::INFO) {
return true;
}
}

private function scheduledTaskFailed(): bool
{
$minutes = $this->configService->getInt(
'FroshTools.config.monitorTaskGraceTime'
);

$date = new \DateTime();
$date->modify(sprintf('-%d minutes', $minutes));

$criteria = new Criteria();
$criteria->addFilter(
new RangeFilter(
'nextExecutionTime',
['lte' => $date->format(\DATE_ATOM)]
)
);
$criteria->addFilter(new NotFilter(
NotFilter::CONNECTION_AND,
[
new EqualsFilter('status', ScheduledTaskDefinition::STATUS_INACTIVE),
]
));

$oldTasks = $this->scheduledTaskRepository
->searchIds($criteria, Context::createDefaultContext())->getIds();

return $oldTasks !== [];
return false;
}
}
6 changes: 3 additions & 3 deletions src/Command/UpdateComposerPluginsCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class UpdateComposerPluginsCommand extends Command
public function __construct(
#[Autowire('%kernel.project_dir%')]
private readonly string $projectDir,
private readonly KernelPluginLoader $pluginLoader
private readonly KernelPluginLoader $pluginLoader,
) {
parent::__construct();
$this->application = new Application();
Expand Down Expand Up @@ -51,7 +51,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'--working-dir' => $this->projectDir,
'--direct' => null,
'--format' => 'json',
]
],
);

$this->application->run($composerinput, $composerOutput);
Expand Down Expand Up @@ -87,7 +87,7 @@ protected function execute(InputInterface $input, OutputInterface $output): int
'command' => 'update',
'--working-dir' => $this->projectDir,
'packages' => $updates,
]
],
);
$this->application->run($composerUpdate, $composerOutput);

Expand Down
2 changes: 1 addition & 1 deletion src/Components/CacheHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ private static function getSizeFallback(string $path): int
$dirIterator = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS | \FilesystemIterator::SKIP_DOTS);
$iterator = new \RecursiveIteratorIterator(
$dirIterator,
\RecursiveIteratorIterator::LEAVES_ONLY
\RecursiveIteratorIterator::LEAVES_ONLY,
);

$size = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ final class AdminInfoSubscriberEventListener
{
public function __construct(
#[Autowire('%frosh_tools.elasticsearch.enabled%')]
private readonly bool $elasticsearchEnabled
private readonly bool $elasticsearchEnabled,
) {}

public function __invoke(ResponseEvent $event): void
Expand Down
2 changes: 1 addition & 1 deletion src/Components/Elasticsearch/ElasticsearchManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public function __construct(
private readonly ElasticsearchOutdatedIndexDetector $outdatedIndexDetector,
private readonly Connection $connection,
#[Autowire(service: 'shopware.increment.gateway.registry')]
private readonly IncrementGatewayRegistry $gatewayRegistry
private readonly IncrementGatewayRegistry $gatewayRegistry,
) {}

public function isEnabled(): bool
Expand Down
10 changes: 5 additions & 5 deletions src/Components/Health/Checker/HealthChecker/MysqlChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ private function checkMariadbVersion(HealthCollection $collection, string $versi
'mysql',
'MariaDB Version',
$version,
'min ' . $minVersion
'min ' . $minVersion,
));
}
}
Expand All @@ -69,7 +69,7 @@ private function checkMysqlVersion(HealthCollection $collection, string $version
'mysql',
$snippet,
$version,
$recommended
$recommended,
));

return;
Expand All @@ -80,7 +80,7 @@ private function checkMysqlVersion(HealthCollection $collection, string $version
'mysql',
$snippet,
$version,
$recommended
$recommended,
));

return;
Expand All @@ -90,7 +90,7 @@ private function checkMysqlVersion(HealthCollection $collection, string $version
'mysql',
$snippet,
$version,
'min ' . $minVersion
'min ' . $minVersion,
));
}

Expand All @@ -115,7 +115,7 @@ private static function getVersionNumber(string $versionString): string
if (!preg_match(
'/^(?:5\.5\.5-)?(mariadb-)?(?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)/i',
$versionString,
$versionParts
$versionParts,
)) {
throw new \RuntimeException(sprintf('Invalid version string: %s', $versionString));
}
Expand Down
20 changes: 10 additions & 10 deletions src/Components/Health/Checker/HealthChecker/PhpChecker.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ private function checkPhp(HealthCollection $collection): void
'php-version',
'PHP Version',
$currentPhpVersion,
'min ' . $minPhpVersion
)
'min ' . $minPhpVersion,
),
);

return;
Expand All @@ -49,8 +49,8 @@ private function checkPhp(HealthCollection $collection): void
'php-version',
'PHP Version',
$currentPhpVersion,
'min ' . $minPhpVersion
)
'min ' . $minPhpVersion,
),
);
}

Expand All @@ -64,8 +64,8 @@ private function checkMaxExecutionTime(HealthCollection $collection): void
'php-max-execution',
'Max-Execution-Time',
(string) $currentMaxExecutionTime,
'min ' . $minMaxExecutionTime
)
'min ' . $minMaxExecutionTime,
),
);

return;
Expand All @@ -75,7 +75,7 @@ private function checkMaxExecutionTime(HealthCollection $collection): void
'php-max-execution',
'Max-Execution-Time',
(string) $currentMaxExecutionTime,
'min ' . $minMaxExecutionTime
'min ' . $minMaxExecutionTime,
));
}

Expand All @@ -91,8 +91,8 @@ private function checkMemoryLimit(HealthCollection $collection): void
'php-memory-limit',
'Memory-Limit',
$this->formatSize($currentMemoryLimit),
'min ' . $this->formatSize($minMemoryLimit)
)
'min ' . $this->formatSize($minMemoryLimit),
),
);

return;
Expand All @@ -102,7 +102,7 @@ private function checkMemoryLimit(HealthCollection $collection): void
'php-memory-limit',
'Memory-Limit',
$this->formatSize($currentMemoryLimit),
'min ' . $this->formatSize($minMemoryLimit)
'min ' . $this->formatSize($minMemoryLimit),
));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ProductionChecker implements HealthCheckerInterface, CheckerInterface
{
public function __construct(
#[Autowire('%kernel.environment%')]
private readonly string $environment
private readonly string $environment,
) {}

public function collect(HealthCollection $collection): void
Expand Down
Loading