CronReadinessCheck.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. <?php
  2. /**
  3. * Copyright © 2013-2017 Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Update;
  7. /**
  8. * This class is used by Updater cron script to check if it can be run properly
  9. */
  10. class CronReadinessCheck
  11. {
  12. /**
  13. * Basename to Updater status file
  14. */
  15. const CRON_JOB_STATUS_FILE = '.update_cronjob_status';
  16. const UPDATE_CRON_LOG_FILE = 'var/log/update.log';
  17. /**#@+
  18. * Keys used in status file
  19. */
  20. const KEY_READINESS_CHECKS = 'readiness_checks';
  21. const KEY_FILE_PERMISSIONS_VERIFIED = 'file_permissions_verified';
  22. const KEY_ERROR = 'error';
  23. const KEY_CURRENT_TIMESTAMP = 'current_timestamp';
  24. const KEY_LAST_TIMESTAMP = 'last_timestamp';
  25. /**#@-*/
  26. /**
  27. * Setup cron job status file name
  28. */
  29. const SETUP_CRON_JOB_STATUS_FILE = '.setup_cronjob_status';
  30. /**#@+
  31. * Keys from .setup_cronjob_status file
  32. */
  33. const KEY_FILE_PATHS = 'file_paths';
  34. const KEY_LIST = 'list';
  35. /**#@-*/
  36. /**
  37. * Run Cron job readiness check
  38. *
  39. * @return bool
  40. */
  41. public function runReadinessCheck()
  42. {
  43. $resultJsonRawData = ['readiness_checks' => []];
  44. $success = true;
  45. $permissionInfo = $this->checkPermissionsRecursively();
  46. if ($permissionInfo->containsPaths())
  47. {
  48. $error = '';
  49. $outputString = '';
  50. if (!empty($permissionInfo->getNonWritablePaths())) {
  51. $error .= '<br/>Found non-writable path(s):<br/>' .
  52. implode('<br/>', $permissionInfo->getNonWritablePaths());
  53. $outputString = 'Cron readiness check failure! Found non-writable paths:'
  54. . PHP_EOL
  55. . "\t"
  56. . implode(PHP_EOL . "\t", $permissionInfo->getNonWritablePaths());
  57. }
  58. if (!empty($permissionInfo->getNonReadablePaths())) {
  59. $error .= '<br/>Found non-readable path(s):<br/>' .
  60. implode('<br/>', $permissionInfo->getNonReadablePaths());
  61. $outputString .= PHP_EOL
  62. . 'Cron readiness check failure! Found non-readable paths:'
  63. . PHP_EOL
  64. . "\t"
  65. . implode(PHP_EOL . "\t", $permissionInfo->getNonReadablePaths());
  66. }
  67. $resultJsonRawData[self::KEY_READINESS_CHECKS][self::KEY_ERROR] = $error;
  68. $resultJsonRawData[self::KEY_READINESS_CHECKS][self::KEY_FILE_PERMISSIONS_VERIFIED] = false;
  69. $success = false;
  70. } else {
  71. $resultJsonRawData[self::KEY_READINESS_CHECKS][self::KEY_FILE_PERMISSIONS_VERIFIED] = true;
  72. }
  73. if (file_exists(MAGENTO_BP . '/var/' . self::CRON_JOB_STATUS_FILE)) {
  74. $jsonData = json_decode(file_get_contents(MAGENTO_BP . '/var/' . self::CRON_JOB_STATUS_FILE), true);
  75. if (isset($jsonData[self::KEY_CURRENT_TIMESTAMP])) {
  76. $resultJsonRawData[self::KEY_LAST_TIMESTAMP] = $jsonData[self::KEY_CURRENT_TIMESTAMP];
  77. }
  78. }
  79. $resultJsonRawData[self::KEY_CURRENT_TIMESTAMP] = time();
  80. $resultJson = json_encode($resultJsonRawData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
  81. file_put_contents(MAGENTO_BP . '/var/' . self::CRON_JOB_STATUS_FILE, $resultJson);
  82. // If non-accessible paths are found, log an 'error' entry for the same in update.log
  83. if ( !$success && !empty($outputString) ) {
  84. $updateLoggerFactory = new UpdateLoggerFactory();
  85. $logger = $updateLoggerFactory->create();
  86. $logger->log(\Psr\Log\LogLevel::ERROR, $outputString);
  87. }
  88. return $success;
  89. }
  90. /**
  91. * Check file permissions recursively
  92. *
  93. * @return PermissionInfo
  94. */
  95. private function checkPermissionsRecursively()
  96. {
  97. // For backward compatibility, initialize the list wth magento root directory.
  98. $dirAndFileList[] = '';
  99. // Get the list of magento specific directories and files
  100. $setupCronJobStatusFilePath = MAGENTO_BP . '/var/' . self::SETUP_CRON_JOB_STATUS_FILE;
  101. if (is_readable($setupCronJobStatusFilePath)) {
  102. $fileContents = json_decode(file_get_contents($setupCronJobStatusFilePath), true);
  103. if (isset($fileContents[self::KEY_FILE_PATHS][self::KEY_LIST])) {
  104. $dirAndFileList = $fileContents[self::KEY_FILE_PATHS][self::KEY_LIST];
  105. }
  106. }
  107. $nonWritablePaths = [];
  108. $nonReadablePaths = [];
  109. foreach ($dirAndFileList as $path) {
  110. $path = MAGENTO_BP . '/' . $path;
  111. if (is_dir($path)) {
  112. try {
  113. $filesystemIterator = new \RecursiveIteratorIterator(
  114. new \RecursiveDirectoryIterator($path),
  115. \RecursiveIteratorIterator::SELF_FIRST
  116. );
  117. $filesystemIterator = new ExcludeFilter(
  118. $filesystemIterator,
  119. [
  120. MAGENTO_BP . '/update',
  121. MAGENTO_BP . '/var/session',
  122. '.git',
  123. '.idea'
  124. ]
  125. );
  126. foreach ($filesystemIterator as $item) {
  127. $path = $item->__toString();
  128. if (!is_writable($path)) {
  129. $nonWritablePaths[] = $path;
  130. }
  131. }
  132. } catch (\UnexpectedValueException $e) {
  133. $nonReadablePaths[] = $path;
  134. }
  135. } else {
  136. if (!is_writable($path)) {
  137. $nonWritablePaths[] = $path;
  138. }
  139. }
  140. }
  141. return new PermissionInfo($nonWritablePaths, $nonReadablePaths);
  142. }
  143. }