TikiWiki/tiki-manager

View on GitHub
src/Command/SetupSchedulerCronInstanceCommand.php

Summary

Maintainability
C
7 hrs
Test Coverage
<?php

namespace TikiManager\Command;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use TikiManager\Application\Instance;
use TikiManager\Command\Helper\CommandHelper;

class SetupSchedulerCronInstanceCommand extends TikiManagerCommand
{
    protected function configure()
    {
        parent::configure();

        $this
            ->setName('instance:setup-scheduler-cron')
            ->setDescription('Setup instance\'s scheduler cron')
            ->setHelp('This command allows you to enable the cron to run the schedulers')
            ->addOption(
                'instances',
                'i',
                InputOption::VALUE_OPTIONAL,
                'List of instance IDs to be checked, separated by comma (,)'
            )
            ->addOption(
                'time',
                't',
                InputOption::VALUE_REQUIRED,
                'Cron time string',
                '* * * * *'
            )
            ->addOption(
                'update',
                'u',
                InputOption::VALUE_NONE,
                'Update existing configured cron'
            )
            ->addOption(
                'check',
                'c',
                InputOption::VALUE_NONE,
                'Check if there is cronjob configured'
            )
            ->addOption(
                'enable',
                null,
                InputOption::VALUE_NONE,
                'Enable existing cronjob'
            )
            ->addOption(
                'disable',
                null,
                InputOption::VALUE_NONE,
                'Disable existing cronjob'
            );
    }

    protected function interact(InputInterface $input, OutputInterface $output)
    {
        $instances = CommandHelper::getInstances('all', true);
        $instancesInfo = CommandHelper::getInstancesInfo($instances);

        if (empty($instancesInfo)) {
            return;
        }

        if (empty($input->getOption('instances'))) {
            $this->io->newLine();
            $output->writeln('<comment>In case you want to configure more than one instance, please use a comma (,) between the values</comment>');

            $this->io->newLine();
            CommandHelper::renderInstancesTable($output, $instancesInfo);

            $selectedInstances = $this->io->ask(
                'Which instance(s) do you want to setup scheduler cron?',
                null,
                function ($answer) use ($instances) {
                    return CommandHelper::validateInstanceSelection($answer, $instances);
                }
            );

            $selectedInstances = implode(',', array_map(function ($elem) {
                return $elem->getId();
            }, $selectedInstances));

            $input->setOption('instances', $selectedInstances);
        }

        $check = $input->getOption('check');
        $changeStatus = $input->getOption('enable') ? 'enable' : ($input->getOption('disable') ? 'disable' : null);
        if ($check || $changeStatus || $input->getOption('time') != '* * * * *') {
            return;
        }

        $cronFreq = $this->io->choice('What is the cron frequency?', [
            'Every minute (* * * * *)',
            'Every half hour (*/30 * * * *)',
            'Every hour (0 * * * *)',
            'Custom'
        ], 0);

        if ($cronFreq === 'Custom') {
            $cronFreq = $this->io->ask('Please provide a cron time expression', null, function ($answer) {
                return Helper\CommandHelper::validateCrontabInput($answer);
            });
        } else {
            preg_match('/\((.+)\)/', $cronFreq, $matches);
            $cronFreq = $matches[1];
        }

        $input->setOption('time', $cronFreq);
    }

    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $instances = CommandHelper::getInstances();
        $instancesInfo = CommandHelper::getInstancesInfo($instances);

        if (!isset($instancesInfo)) {
            $output->writeln('<comment>No instances available to configure.</comment>');
            return 0;
        }

        $instancesOption = $input->getOption('instances');

        CommandHelper::validateInstanceSelection($instancesOption, $instances);
        $instancesOption = explode(',', $instancesOption);
        $selectedInstances = array_intersect_key($instances, array_flip($instancesOption));

        $time = $input->getOption('time');
        Helper\CommandHelper::validateCrontabInput($time);

        $hookName = $this->getCommandHook();
        foreach ($selectedInstances as $instance) {
            try {
                $cronJob = $this->handleInstance($instance, $input);
                $this->writeCronJobStatus($cronJob);
                $hookName->registerPostHookVars(['instance' => $instance, 'cron' => $cronJob]);
            } catch (\Exception $e) {
                $this->io->writeln('<error>'. $e->getMessage() . '</error>');
            }

            $this->io->newLine();
        }

        return 0;
    }

    protected function handleInstance(Instance $instance, InputInterface $input): Instance\CronJob
    {
        $this->io->section($instance->name);

        $instanceOS = $instance->getDiscovery()->detectOS();
        if ($instanceOS === 'WINDOWS' || $instanceOS === 'WINNT') {
            throw new \Exception('Operating System not supported.');
        }

        $update = $input->getOption('update');
        $check = $input->getOption('check');
        $changeStatus = $input->getOption('enable') ? 'enable' : ($input->getOption('disable') ? 'disable' : null);
        $time = $input->getOption('time');

        $cronManager = $instance->getCronManager();
        $cronJob = $cronManager->getConsoleCommandJob('scheduler:run');

        if ($check) {
            return $cronJob;
        }

        if (!$cronJob) {
            $newCronJob = $cronManager->createConsoleCommandJob($time, 'scheduler:run');
            $cronManager->addJob($newCronJob);
            $this->io->info('Job created');
            return $newCronJob;
        }

        if (!$update && !$changeStatus) {
            $this->io->info('There is a job already configured, please use --update to update the existing job. Skipping...');
            return $cronJob;
        }

        $newCronJob = $cronManager->createConsoleCommandJob($time, 'scheduler:run');

        if ($changeStatus) {
            $newCronJob = clone $cronJob;
            $newCronJob->$changeStatus();

            if ($cronJob->isEnabled() == $newCronJob->isEnabled()) {
                $this->io->info('Existing job is already ' . $changeStatus . 'd. Skipping...');
                return $newCronJob;
            }
        }

        $cronManager->replaceJob($cronJob, $newCronJob);
        $this->io->info('Job updated.');

        return $newCronJob;
    }

    protected function writeCronJobStatus(Instance\CronJob $job = null)
    {
        if (!$job) {
            $message = <<<TXT
Cron Job:
  - Status: <error>%s</error>
TXT;
            $output = sprintf(
                $message,
                'No cronjob was found'
            );
        } else {
            $statusMsg = !$job->isEnabled() ? '<error>Disabled</error>' : '<info>Enabled</info>';
            $message = <<<TXT
Cron Job:
  - Status: %s
  - Time: %s
  - Command: %s
TXT;

            $output = sprintf(
                $message,
                $statusMsg,
                $job->getTime(),
                $job->getCommand()
            );
        }

        $this->io->writeln($output);
    }
}