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
No related merge requests found
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