Skip to content
Snippets Groups Projects
Commit a001dacf authored by Adam G-H's avatar Adam G-H Committed by Ted Bowman
Browse files

Issue #3276255 by phenaproxima: Disable unattended updates until TUF integration is complete

parent 876e0331
No related branches found
No related tags found
1 merge request!291Issue #3276255: Disable Cron Update until TUF integration is complete
Showing
with 176 additions and 89 deletions
......@@ -6,15 +6,11 @@
*/
use Drupal\automatic_updates\BatchProcessor;
use Drupal\automatic_updates\ProjectInfo;
use Drupal\Core\Routing\RouteMatchInterface;
use Drupal\automatic_updates\CronUpdater;
use Drupal\automatic_updates\Validation\AdminReadinessMessages;
use Drupal\Core\Extension\ExtensionVersion;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\system\Controller\DbUpdateController;
use Drupal\update\ProjectSecurityData;
/**
* Implements hook_help().
......@@ -144,52 +140,6 @@ function automatic_updates_form_update_manager_update_form_alter(&$form, FormSta
}
}
/**
* Implements hook_form_FORM_ID_alter() for 'update_settings' form.
*/
function automatic_updates_form_update_settings_alter(array &$form, FormStateInterface $form_state, string $form_id) {
$project_info = new ProjectInfo('drupal');
$version = ExtensionVersion::createFromVersionString($project_info->getInstalledVersion());
$current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
// @todo In https://www.drupal.org/node/2998285 use the update XML to
// determine when the installed of core will become unsupported.
$supported_until_version = $version->getMajorVersion() . '.'
. ((int) $version->getMinorVersion() + ProjectSecurityData::CORE_MINORS_WITH_SECURITY_COVERAGE)
. '.0';
$form['automatic_updates_cron'] = [
'#type' => 'radios',
'#title' => t('Automatically update Drupal core'),
'#options' => [
CronUpdater::DISABLED => t('Disabled'),
CronUpdater::ALL => t('All supported updates'),
CronUpdater::SECURITY => t('Security updates only'),
],
'#default_value' => \Drupal::config('automatic_updates.settings')->get('cron'),
'#description' => t(
'If enabled, Drupal core will be automatically updated when an update is available. Automatic updates are only supported for @current_minor.x versions of Drupal core. Drupal @current_minor will receive security updates until @supported_until_version is released.',
[
'@current_minor' => $current_minor,
'@supported_until_version' => $supported_until_version,
]
),
];
$form += [
'#submit' => ['::submitForm'],
];
$form['#submit'][] = '_automatic_updates_update_settings_form_submit';
}
/**
* Submit function for the 'update_settings' form.
*/
function _automatic_updates_update_settings_form_submit(array &$form, FormStateInterface $form_state) {
\Drupal::configFactory()
->getEditable('automatic_updates.settings')
->set('cron', $form_state->getValue('automatic_updates_cron'))
->save();
}
/**
* Implements hook_local_tasks_alter().
*/
......
......@@ -11,7 +11,6 @@ services:
- '@event_dispatcher'
- '@automatic_updates.updater'
- '@automatic_updates.cron_updater'
- '@config.factory'
- 24
tags:
- { name: event_subscriber }
......@@ -119,6 +118,7 @@ services:
- '@state'
- '@datetime.time'
- '@string_translation'
- '@automatic_updates.cron_updater'
tags:
- { name: event_subscriber }
automatic_updates.validator.staged_database_updates:
......@@ -134,6 +134,6 @@ services:
tags:
- { name: event_subscriber }
automatic_updates.validator.target_release:
class: \Drupal\automatic_updates\Validator\UpdateReleaseValidator
class: Drupal\automatic_updates\Validator\UpdateReleaseValidator
tags:
- { name: event_subscriber }
......@@ -14,6 +14,15 @@ use Drupal\package_manager\Exception\StageValidationException;
*/
class CronUpdater extends Updater {
/**
* Whether or not cron updates are hard-disabled.
*
* @var bool
*
* @todo Remove this when TUF integration is stable.
*/
private static $disabled = TRUE;
/**
* All automatic updates are disabled.
*
......@@ -69,7 +78,7 @@ class CronUpdater extends Updater {
* Handles updates during cron.
*/
public function handleCron(): void {
if ($this->isDisabled()) {
if ($this->getMode() === static::DISABLED) {
return;
}
......@@ -134,13 +143,25 @@ class CronUpdater extends Updater {
}
/**
* Determines if cron updates are disabled.
* Gets the cron update mode.
*
* @return string
* The cron update mode. Will be one of the following constants:
* - \Drupal\automatic_updates\CronUpdater::DISABLED if updates during cron
* are entirely disabled.
* - \Drupal\automatic_updates\CronUpdater::SECURITY only security updates
* can be done during cron.
* - \Drupal\automatic_updates\CronUpdater::ALL if all updates are allowed
* during cron.
*
* @return bool
* TRUE if cron updates are disabled, otherwise FALSE.
* @todo Make this always return a string, with a sensible default, in
* https://www.drupal.org/i/3276534.
*/
private function isDisabled(): bool {
return $this->configFactory->get('automatic_updates.settings')->get('cron') === static::DISABLED;
final public function getMode(): ?string {
if (self::$disabled) {
return static::DISABLED;
}
return $this->configFactory->get('automatic_updates.settings')->get('cron');
}
}
......@@ -3,7 +3,6 @@
namespace Drupal\automatic_updates\Validation;
use Drupal\automatic_updates\CronUpdater;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
use Drupal\Core\Messenger\MessengerInterface;
use Drupal\Core\Messenger\MessengerTrait;
......@@ -60,11 +59,11 @@ final class AdminReadinessMessages implements ContainerInjectionInterface {
protected $currentRouteMatch;
/**
* The config factory service.
* The cron updater service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
* @var \Drupal\automatic_updates\CronUpdater
*/
protected $config;
protected $cronUpdater;
/**
* Constructs a ReadinessRequirement object.
......@@ -81,17 +80,17 @@ final class AdminReadinessMessages implements ContainerInjectionInterface {
* The translation service.
* @param \Drupal\Core\Routing\CurrentRouteMatch $current_route_match
* The current route match.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* The config factory service.
* @param \Drupal\automatic_updates\CronUpdater $cron_updater
* The cron updater service.
*/
public function __construct(ReadinessValidationManager $readiness_checker_manager, MessengerInterface $messenger, AdminContext $admin_context, AccountProxyInterface $current_user, TranslationInterface $translation, CurrentRouteMatch $current_route_match, ConfigFactoryInterface $config) {
public function __construct(ReadinessValidationManager $readiness_checker_manager, MessengerInterface $messenger, AdminContext $admin_context, AccountProxyInterface $current_user, TranslationInterface $translation, CurrentRouteMatch $current_route_match, CronUpdater $cron_updater) {
$this->readinessCheckerManager = $readiness_checker_manager;
$this->setMessenger($messenger);
$this->adminContext = $admin_context;
$this->currentUser = $current_user;
$this->setStringTranslation($translation);
$this->currentRouteMatch = $current_route_match;
$this->config = $config;
$this->cronUpdater = $cron_updater;
}
/**
......@@ -105,7 +104,7 @@ final class AdminReadinessMessages implements ContainerInjectionInterface {
$container->get('current_user'),
$container->get('string_translation'),
$container->get('current_route_match'),
$container->get('config.factory')
$container->get('automatic_updates.cron_updater')
);
}
......@@ -142,7 +141,7 @@ final class AdminReadinessMessages implements ContainerInjectionInterface {
protected function displayResultsOnCurrentPage(): bool {
// If updates will not run during cron then we don't need to show the
// readiness checks on admin pages.
if ($this->config->get('automatic_updates.settings')->get('cron') === CronUpdater::DISABLED) {
if ($this->cronUpdater->getMode() === CronUpdater::DISABLED) {
return FALSE;
}
......
......@@ -6,7 +6,6 @@ use Drupal\automatic_updates\CronUpdater;
use Drupal\automatic_updates\Event\ReadinessCheckEvent;
use Drupal\automatic_updates\Updater;
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
use Drupal\package_manager\Event\PostApplyEvent;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
......@@ -60,13 +59,6 @@ class ReadinessValidationManager implements EventSubscriberInterface {
*/
protected $cronUpdater;
/**
* The config factory service.
*
* @var \Drupal\Core\Config\ConfigFactoryInterface
*/
protected $config;
/**
* Constructs a ReadinessValidationManager.
*
......@@ -80,18 +72,15 @@ class ReadinessValidationManager implements EventSubscriberInterface {
* The updater service.
* @param \Drupal\automatic_updates\CronUpdater $cron_updater
* The cron updater service.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config
* The config factory service.
* @param int $results_time_to_live
* The number of hours to store results.
*/
public function __construct(KeyValueExpirableFactoryInterface $key_value_expirable_factory, TimeInterface $time, EventDispatcherInterface $dispatcher, Updater $updater, CronUpdater $cron_updater, ConfigFactoryInterface $config, int $results_time_to_live) {
public function __construct(KeyValueExpirableFactoryInterface $key_value_expirable_factory, TimeInterface $time, EventDispatcherInterface $dispatcher, Updater $updater, CronUpdater $cron_updater, int $results_time_to_live) {
$this->keyValueExpirable = $key_value_expirable_factory->get('automatic_updates');
$this->time = $time;
$this->eventDispatcher = $dispatcher;
$this->updater = $updater;
$this->cronUpdater = $cron_updater;
$this->config = $config;
$this->resultsTimeToLive = $results_time_to_live;
}
......@@ -104,7 +93,7 @@ class ReadinessValidationManager implements EventSubscriberInterface {
// If updates will run during cron, use the cron updater service provided by
// this module. This will allow subscribers to ReadinessCheckEvent to run
// specific validation for conditions that only affect cron updates.
if ($this->config->get('automatic_updates.settings')->get('cron') === CronUpdater::DISABLED) {
if ($this->cronUpdater->getMode() === CronUpdater::DISABLED) {
$stage = $this->updater;
}
else {
......
......@@ -75,6 +75,13 @@ class CronFrequencyValidator implements EventSubscriberInterface {
*/
protected $time;
/**
* The cron updater service.
*
* @var \Drupal\automatic_updates\CronUpdater
*/
protected $cronUpdater;
/**
* CronFrequencyValidator constructor.
*
......@@ -88,13 +95,16 @@ class CronFrequencyValidator implements EventSubscriberInterface {
* The time service.
* @param \Drupal\Core\StringTranslation\TranslationInterface $translation
* The translation service.
* @param \Drupal\automatic_updates\CronUpdater $cron_updater
* The cron updater service.
*/
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, StateInterface $state, TimeInterface $time, TranslationInterface $translation) {
public function __construct(ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, StateInterface $state, TimeInterface $time, TranslationInterface $translation, CronUpdater $cron_updater) {
$this->configFactory = $config_factory;
$this->moduleHandler = $module_handler;
$this->state = $state;
$this->time = $time;
$this->setStringTranslation($translation);
$this->cronUpdater = $cron_updater;
}
/**
......@@ -104,12 +114,9 @@ class CronFrequencyValidator implements EventSubscriberInterface {
* The event object.
*/
public function checkCronFrequency(ReadinessCheckEvent $event): void {
$cron_enabled = $this->configFactory->get('automatic_updates.settings')
->get('cron');
// If automatic updates are disabled during cron, there's nothing we need
// to validate.
if ($cron_enabled === CronUpdater::DISABLED) {
if ($this->cronUpdater->getMode() === CronUpdater::DISABLED) {
return;
}
elseif ($this->moduleHandler->moduleExists('automated_cron')) {
......
......@@ -91,10 +91,14 @@ final class CronUpdateVersionValidator extends UpdateVersionValidator {
]);
}
// We cannot use dependency injection to get the cron updater because that
// would create a circular service dependency.
$level = \Drupal::service('automatic_updates.cron_updater')
->getMode();
// If both the from and to version numbers are valid check if the current
// settings only allow security updates during cron and if so ensure the
// update release is a security release.
$level = $this->configFactory->get('automatic_updates.settings')->get('cron');
if ($level === CronUpdater::SECURITY) {
$releases = (new ProjectInfo('drupal'))->getInstallableReleases();
// @todo Remove this check and add validation to
......
name: 'Automatic Updates Test: Cron'
type: module
description: 'Enables cron updates for testing purposes.'
package: Testing
<?php
/**
* @file
* Contains hook implementations to enable automatic updates during cron.
*
* @todo Move into automatic_updates when TUF integration is stable.
*/
use Drupal\automatic_updates\ProjectInfo;
use Drupal\automatic_updates\CronUpdater;
use Drupal\Core\Extension\ExtensionVersion;
use Drupal\Core\Form\FormStateInterface;
use Drupal\update\ProjectSecurityData;
/**
* Implements hook_form_FORM_ID_alter() for 'update_settings' form.
*/
function automatic_updates_test_cron_form_update_settings_alter(array &$form, FormStateInterface $form_state, string $form_id) {
$project_info = new ProjectInfo('drupal');
$version = ExtensionVersion::createFromVersionString($project_info->getInstalledVersion());
$current_minor = $version->getMajorVersion() . '.' . $version->getMinorVersion();
// @todo In https://www.drupal.org/node/2998285 use the update XML to
// determine when the installed of core will become unsupported.
$supported_until_version = $version->getMajorVersion() . '.'
. ((int) $version->getMinorVersion() + ProjectSecurityData::CORE_MINORS_WITH_SECURITY_COVERAGE)
. '.0';
$form['automatic_updates_cron'] = [
'#type' => 'radios',
'#title' => t('Automatically update Drupal core'),
'#options' => [
CronUpdater::DISABLED => t('Disabled'),
CronUpdater::ALL => t('All supported updates'),
CronUpdater::SECURITY => t('Security updates only'),
],
'#default_value' => \Drupal::config('automatic_updates.settings')->get('cron'),
'#description' => t(
'If enabled, Drupal core will be automatically updated when an update is available. Automatic updates are only supported for @current_minor.x versions of Drupal core. Drupal @current_minor will receive security updates until @supported_until_version is released.',
[
'@current_minor' => $current_minor,
'@supported_until_version' => $supported_until_version,
]
),
];
$form += [
'#submit' => ['::submitForm'],
];
$form['#submit'][] = '_automatic_updates_test_cron_update_settings_form_submit';
}
/**
* Submit function for the 'update_settings' form.
*/
function _automatic_updates_test_cron_update_settings_form_submit(array &$form, FormStateInterface $form_state) {
\Drupal::configFactory()
->getEditable('automatic_updates.settings')
->set('cron', $form_state->getValue('automatic_updates_cron'))
->save();
}
services:
automatic_updates_test_cron.enabler:
class: Drupal\automatic_updates_test_cron\Enabler
tags:
- { name: event_subscriber }
<?php
namespace Drupal\automatic_updates_test_cron;
use Drupal\automatic_updates\CronUpdater;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\KernelEvents;
/**
* Enables automatic updates during cron.
*
* @todo Remove this when TUF integration is stable.
*/
class Enabler implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
KernelEvents::REQUEST => 'enableCron',
];
}
/**
* Enables automatic updates during cron.
*/
public function enableCron(): void {
if (class_exists(CronUpdater::class)) {
$reflector = new \ReflectionClass(CronUpdater::class);
$reflector = $reflector->getProperty('disabled');
$reflector->setAccessible(TRUE);
$reflector->setValue(NULL, FALSE);
}
}
}
......@@ -39,6 +39,7 @@ abstract class UpdateTestBase extends TemplateProjectTestBase {
$this->installModules([
'automatic_updates',
'automatic_updates_test',
'automatic_updates_test_cron',
'automatic_updates_test_release_history',
]);
}
......
......@@ -15,6 +15,7 @@ abstract class AutomaticUpdatesFunctionalTestBase extends BrowserTestBase {
* {@inheritdoc}
*/
protected static $modules = [
'automatic_updates_test_cron',
'automatic_updates_test_disable_validators',
'package_manager_bypass',
];
......
......@@ -24,7 +24,12 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa
/**
* {@inheritdoc}
*/
protected static $modules = ['system', 'update', 'update_test'];
protected static $modules = [
'automatic_updates_test_cron',
'system',
'update',
'update_test',
];
/**
* The mocked HTTP client that returns metadata about available updates.
......@@ -68,6 +73,9 @@ abstract class AutomaticUpdatesKernelTestBase extends PackageManagerKernelTestBa
// from a sane state.
// @see \Drupal\automatic_updates\Validator\CronFrequencyValidator
$this->container->get('state')->set('system.cron_last', time());
// @todo Remove this when TUF integration is stable.
$this->container->get('automatic_updates_test_cron.enabler')->enableCron();
}
/**
......
......@@ -34,7 +34,8 @@ class CronFrequencyValidatorTest extends AutomaticUpdatesKernelTestBase {
$this->container->get('module_handler'),
$this->container->get('state'),
$this->container->get('datetime.time'),
$this->container->get('string_translation')
$this->container->get('string_translation'),
$this->container->get('automatic_updates.cron_updater')
) extends CronFrequencyValidator {
/**
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment