diff --git a/core/modules/auto_updates/auto_updates.install b/core/modules/auto_updates/auto_updates.install
index 6799a1c510..173d1ceaef 100644
--- a/core/modules/auto_updates/auto_updates.install
+++ b/core/modules/auto_updates/auto_updates.install
@@ -18,7 +18,7 @@ function auto_updates_requirements($phase) {
}
/** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
- $checker_manager = \Drupal::service('auto_updates.readiness_checker');
+ $checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
if (!$checker_manager->isEnabled()) {
return [];
}
@@ -75,6 +75,6 @@ function auto_updates_requirements($phase) {
*/
function auto_updates_install($is_syncing) {
/** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
- $checker_manager = \Drupal::service('auto_updates.readiness_checker');
+ $checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
$checker_manager->run();
}
diff --git a/core/modules/auto_updates/auto_updates.module b/core/modules/auto_updates/auto_updates.module
index 7fec2f650f..70a68230aa 100644
--- a/core/modules/auto_updates/auto_updates.module
+++ b/core/modules/auto_updates/auto_updates.module
@@ -32,7 +32,7 @@ function auto_updates_page_top(array &$page_top) {
if (in_array(\Drupal::routeMatch()->getRouteName(), $disabled_routes, TRUE)) {
return;
}
- $last_check_timestamp = \Drupal::service('auto_updates.readiness_checker')->timestamp();
+ $last_check_timestamp = \Drupal::service('auto_updates.readiness_checker_manager')->timestamp();
if (\Drupal::time()->getRequestTime() > $last_check_timestamp + ReadinessCheckerManagerInterface::LAST_CHECKED_WARNING) {
$readiness_settings = Url::fromRoute('auto_updates.settings');
\Drupal::messenger()->addError(t('Your site has not recently run an update readiness check. Administer automatic updates and run readiness checks manually.', [
@@ -40,7 +40,7 @@ function auto_updates_page_top(array &$page_top) {
]));
}
/** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
- $checker_manager = \Drupal::service('auto_updates.readiness_checker');
+ $checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
$results = $checker_manager->getResults(ReadinessCheckerManagerInterface::ERROR);
if ($results) {
\Drupal::messenger()->addError(t('Your site is currently failing readiness checks for automatic updates. It cannot be automatically updated until further action is performed:', [':readiness_checks' => 'https://www.drupal.org/docs/8/update/auto-updates#readiness-checks']));
@@ -64,7 +64,7 @@ function auto_updates_page_top(array &$page_top) {
function auto_updates_cron() {
$request_time = \Drupal::time()->getRequestTime();
/** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
- $checker_manager = \Drupal::service('auto_updates.readiness_checker');
+ $checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
$last_check = $checker_manager->timestamp();
// Only allow cron to run once every hour.
if ($last_check && ($request_time - $last_check) < 3600) {
@@ -72,7 +72,7 @@ function auto_updates_cron() {
}
/** @var \Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface $checker_manager */
- $checker_manager = \Drupal::service('auto_updates.readiness_checker');
+ $checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
$checker_manager->run();
}
@@ -81,7 +81,7 @@ function auto_updates_cron() {
*/
function auto_updates_modules_installed($modules) {
/** @var ReadinessCheckerManagerInterface $checker_manager */
- $checker_manager = \Drupal::service('auto_updates.readiness_checker');
+ $checker_manager = \Drupal::service('auto_updates.readiness_checker_manager');
if ($checker_manager->clearResultsStaleResults()) {
$checker_manager->run();
}
diff --git a/core/modules/auto_updates/auto_updates.routing.yml b/core/modules/auto_updates/auto_updates.routing.yml
index 1616bc5390..ce01cdf9f4 100644
--- a/core/modules/auto_updates/auto_updates.routing.yml
+++ b/core/modules/auto_updates/auto_updates.routing.yml
@@ -14,5 +14,6 @@ auto_updates.update_readiness:
_title: 'Update readiness checking...'
requirements:
_permission: 'administer software updates'
+ _custom_access: '\Drupal\auto_updates\Controller\ReadinessCheckerController::access'
options:
_admin_route: TRUE
diff --git a/core/modules/auto_updates/auto_updates.services.yml b/core/modules/auto_updates/auto_updates.services.yml
index 0be04c9d2e..461fbcd4fb 100644
--- a/core/modules/auto_updates/auto_updates.services.yml
+++ b/core/modules/auto_updates/auto_updates.services.yml
@@ -4,7 +4,7 @@ services:
arguments: ['%app.root%']
tags:
- { name: readiness_checker, category: error}
- auto_updates.readiness_checker:
+ auto_updates.readiness_checker_manager:
class: Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManager
arguments: ['@keyvalue', '@config.factory']
tags:
diff --git a/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php b/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php
index cd5ac996c5..27160de369 100644
--- a/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php
+++ b/core/modules/auto_updates/src/Controller/ReadinessCheckerController.php
@@ -3,6 +3,7 @@
namespace Drupal\auto_updates\Controller;
use Drupal\auto_updates\ReadinessChecker\ReadinessCheckerManagerInterface;
+use Drupal\Core\Access\AccessResult;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\StringTranslation\TranslationInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -40,7 +41,7 @@ public function __construct(ReadinessCheckerManagerInterface $checker_manager, T
*/
public static function create(ContainerInterface $container) {
return new static(
- $container->get('auto_updates.readiness_checker'),
+ $container->get('auto_updates.readiness_checker_manager'),
$container->get('string_translation')
);
}
@@ -58,4 +59,14 @@ public function run() {
return $this->redirect('auto_updates.settings');
}
+ /**
+ * Checks access based on whether the readiness checkers are enabled.
+ *
+ * @return \Drupal\Core\Access\AccessResultInterface
+ * The access result.
+ */
+ public function access() {
+ return AccessResult::allowedIf($this->checkerManager->isEnabled());
+ }
+
}
diff --git a/core/modules/auto_updates/src/Form/SettingsForm.php b/core/modules/auto_updates/src/Form/SettingsForm.php
index a88eaaf14f..27ba01d1ae 100644
--- a/core/modules/auto_updates/src/Form/SettingsForm.php
+++ b/core/modules/auto_updates/src/Form/SettingsForm.php
@@ -31,7 +31,7 @@ class SettingsForm extends ConfigFormBase {
*/
public static function create(ContainerInterface $container) {
$instance = parent::create($container);
- $instance->checkerManager = $container->get('auto_updates.readiness_checker');
+ $instance->checkerManager = $container->get('auto_updates.readiness_checker_manager');
$instance->dateFormatter = $container->get('date.formatter');
return $instance;
}
diff --git a/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerInterface.php b/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerInterface.php
index b559d8fdd7..ca3031e6d4 100644
--- a/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerInterface.php
+++ b/core/modules/auto_updates/src/ReadinessChecker/ReadinessCheckerInterface.php
@@ -3,7 +3,7 @@
namespace Drupal\auto_updates\ReadinessChecker;
/**
- * Interface for objects capable of readiness checking.
+ * Defines an interface for readiness checker services.
*/
interface ReadinessCheckerInterface {
diff --git a/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php b/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php
index 99d81d968e..e42f21c6b3 100644
--- a/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php
+++ b/core/modules/auto_updates/tests/src/Functional/ReadinessCheckerTest.php
@@ -51,12 +51,13 @@ protected function setUp(): void {
*/
public function testReadinessChecksStatusReport() {
$assert = $this->assertSession();
+ $page = $this->getSession()->getPage();
$this->container->get('module_installer')->uninstall(['automated_cron']);
$this->container->get('module_installer')->install(['auto_updates', 'auto_updates_test']);
// If the the site is ready for updates the users will see the same output
- // regardless of the user has permission to run updates.
+ // regardless of whether the user has permission to run updates.
foreach ([$this->reportViewerUser, $this->checkerRunnerUser] as $user) {
$this->drupalLogin($user);
$this->drupalGet('admin/reports/status');
@@ -64,14 +65,14 @@ public function testReadinessChecksStatusReport() {
}
// Confirm a user without the permission to run readiness checks does not
- // have a link to run the checks when the checks need to run again.
+ // have a link to run the checks when the checks need to be run again.
$this->setFakeTime('+2 days');
$this->drupalLogin($this->reportViewerUser);
$this->drupalGet('admin/reports/status');
$this->assertReadinessReportMatches('Your site has not recently checked if it is ready to apply automatic updates. Readiness checks were last run %s ago.');
// Confirm a user with the permission to run readiness checks does have a
- // link to run the checks when the checks need to run again.
+ // link to run the checks when the checks need to be run again.
$this->drupalLogin($this->checkerRunnerUser);
$this->drupalGet('admin/reports/status');
$this->assertReadinessReportMatches('Your site has not recently checked if it is ready to apply automatic updates.'
@@ -83,6 +84,7 @@ public function testReadinessChecksStatusReport() {
// @todo If coming from the status report page should you be redirected there?
// This is how 'Run cron" works.
$assert->addressEquals('/admin/config/auto_updates');
+ $assert->checkboxChecked('enable_readiness_checks');
$assert->pageTextNotContains("Access denied");
$assert->pageTextContains('Your site is currently failing readiness checks for automatic updates. It cannot be automatically updated until further action is performed:');
$assert->pageTextContains('OMG 🚒. Your server is on 🔥!');
@@ -92,9 +94,39 @@ public function testReadinessChecksStatusReport() {
// Confirm the error is displayed on the status report page.
$this->drupalGet('admin/reports/status');
$this->assertReadinessReportMatches('1 check failed: OMG 🚒. Your server is on 🔥!');
- // @todo Should always show when the checks were last run and a link to
+ // @todo Should we always show when the checks were last run and a link to
// run when there is an error?
}
+
+ // Disable readiness checks.
+ $this->drupalLogin($this->checkerRunnerUser);
+ $this->drupalGet('admin/config/auto_updates');
+ $page->uncheckField('enable_readiness_checks');
+ $page->pressButton('Save configuration');
+
+ // Confirm that when readiness checkers are disabled no information on the
+ // last run is displayed.
+ $assert->pageTextNotContains('Readiness checks have never been run.');
+ $assert->pageTextNotContains('Readiness checks were last run');
+ $assert->pageTextNotContains('>run the readiness checks');
+
+ // Confirm that access is denied when manually going to the readiness
+ // checker controller.
+ $this->drupalGet('admin/config/auto_updates/readiness');
+ $assert->pageTextContains('Access denied');
+
+ $this->drupalGet('admin/reports/status');
+ $assert->pageTextNotContains('Update readiness checks');
+
+ // Re-enable readiness checks.
+ $this->drupalGet('admin/config/auto_updates');
+ $page->checkField('enable_readiness_checks');
+ $page->pressButton('Save configuration');
+
+ // Confirm that the last message displayed is displayed again.
+ $this->drupalGet('admin/reports/status');
+ $this->assertReadinessReportMatches('1 check failed: OMG 🚒. Your server is on 🔥!');
+
}
/**
@@ -121,6 +153,8 @@ public function testReadinessCheckAfterInstall() {
$this->container->get('state')->set(TestChecker::STATE_KEY, 'Security has been compromised. "pass123" was a bad password!');
$this->container->get('module_installer')->install(['color']);
$this->drupalGet('admin/reports/status');
+ // Confirm that new checker message is not displayed because the checker was
+ // not run again.
$this->assertReadinessReportMatches('1 check failed: 😿Oh no! A hacker now owns your files!');
// Confirm the new message is displayed after running the checkers manually.
@@ -138,6 +172,8 @@ public function testReadinessCheckAfterInstall() {
*/
public function testCronRun() {
$assert = $this->assertSession();
+ $page = $this->getSession()->getPage();
+
$this->drupalLogin($this->reportViewerUser);
$this->container->get('module_installer')->install(['auto_updates', 'auto_updates_test']);
$this->drupalGet('admin/reports/status');
@@ -163,10 +199,40 @@ public function testCronRun() {
$this->setFakeTime('+125 minutes');
$this->clickLink('Run cron');
$this->assertReadinessReportMatches('1 check failed: OMG 💦. Now your server is filled with water!');
+
+ // Disable readiness checks.
+ $this->drupalLogin($this->checkerRunnerUser);
+ $this->drupalGet('admin/config/auto_updates');
+ $page->uncheckField('enable_readiness_checks');
+ $page->pressButton('Save configuration');
+
+ // Run cron while readiness checks are disabled.
+ $this->container->get('state')->set(TestChecker::STATE_KEY, '😨 Now your hard drive is missing! How is that even possible?');
+ $this->setFakeTime('+190 minutes');
+ $this->drupalGet('admin/reports/status');
+ $assert->pageTextNotContains('Update readiness checks');
+ $this->clickLink('Run cron');
+ $assert->pageTextNotContains('Update readiness checks');
+
+ // Re-enable readiness checks.
+ $this->drupalGet('admin/config/auto_updates');
+ $page->checkField('enable_readiness_checks');
+ $page->pressButton('Save configuration');
+
+ // Confirm that the new test message for the test readiness checker is not
+ // displayed because the checkers were not run during cron when they were
+ // disabled.
+ $this->drupalGet('admin/reports/status');
+ $this->assertReadinessReportMatches('1 check failed: OMG 💦. Now your server is filled with water!');
+
+ // Confirm running cron displays the new message.
+ $this->setFakeTime('+255 minutes');
+ $this->clickLink('Run cron');
+ $this->assertReadinessReportMatches('1 check failed: 😨 Now your hard drive is missing! How is that even possible?');
}
/**
- * Sets a fake time that will be used in that test.
+ * Sets a fake time that will be used in the test.
*
* @param string $offset
* A date/time offset string.
diff --git a/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php b/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php
index 633b774a30..51a09e5aad 100644
--- a/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php
+++ b/core/modules/auto_updates/tests/src/Kernel/ReadinessChecker/DiskSpaceTest.php
@@ -32,11 +32,16 @@ public function testDiskSpace() {
$disk_space = new TestDiskSpace($this->container->getParameter('app.root'));
$messages = $disk_space->run();
$this->assertCount(2, $messages);
+ $this->assertStringMatchesFormat('Logical disk "%s" has insufficient space. There must be at least %s megabytes free.', (string) $messages[0]);
+ $this->assertStringMatchesFormat('Directory "%s" has insufficient space. There must be at least %s megabytes free.', (string) $messages[1]);
// Out of space not the same logical disk.
$disk_space = new TestDiskSpaceNonSameDisk($this->container->getParameter('app.root'));
$messages = $disk_space->run();
$this->assertCount(3, $messages);
+ $this->assertStringMatchesFormat('Drupal root filesystem "%s" has insufficient space. There must be at least %s megabytes free.', (string) $messages[0]);
+ $this->assertStringMatchesFormat('Vendor filesystem "%s" has insufficient space. There must be at least %s megabytes free.', (string) $messages[1]);
+ $this->assertStringMatchesFormat('Directory "%s" has insufficient space. There must be at least %s megabytes free.', (string) $messages[2]);
}
}