diff --git a/resources/sql/autopatches/20150205.daemonenv.sql b/resources/sql/autopatches/20150205.daemonenv.sql new file mode 100644 index 0000000000..fccf6f0810 --- /dev/null +++ b/resources/sql/autopatches/20150205.daemonenv.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_daemon.daemon_log + ADD envInfo LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}; diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index 83c1a26ea4..d7c93dc03b 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -92,11 +92,36 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { 'At least one daemon is currently running with different '. 'configuration than the Phabricator web application.'); + $list_section = null; + $env_info = $daemon->getEnvInfo(); + if ($env_info) { + $issues = PhabricatorEnv::compareEnvironmentInfo( + PhabricatorEnv::calculateEnvironmentInfo(), + $env_info); + + if ($issues) { + foreach ($issues as $key => $issue) { + $issues[$key] = phutil_tag('li', array(), $issue); + } + + $list_section = array( + pht( + 'The configurations differ in the following %s way(s):', + new PhutilNumber(count($issues))), + phutil_tag( + 'ul', + array(), + $issues), + ); + } + } + + $message = pht( 'At least one daemon is currently running with a different '. 'configuration (config checksum %s) than the web application '. '(config checksum %s).'. - "\n\n". + "\n\n%s". 'This usually means that you have just made a configuration change '. 'from the web UI, but have not yet restarted the daemons. You '. 'need to restart the daemons after making configuration changes '. @@ -130,6 +155,7 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { phutil_tag('tt', array(), substr($daemon->getEnvHash(), 0, 12)), phutil_tag('tt', array(), substr($environment_hash, 0, 12)), + $list_section, phutil_tag('tt', array(), 'bin/phd restart'), phutil_tag( 'a', diff --git a/src/applications/daemon/event/PhabricatorDaemonEventListener.php b/src/applications/daemon/event/PhabricatorDaemonEventListener.php index 2ed1540790..47c543907b 100644 --- a/src/applications/daemon/event/PhabricatorDaemonEventListener.php +++ b/src/applications/daemon/event/PhabricatorDaemonEventListener.php @@ -42,6 +42,7 @@ final class PhabricatorDaemonEventListener extends PhabricatorEventListener { ->setPID(getmypid()) ->setRunningAsUser($current_user['name']) ->setEnvHash(PhabricatorEnv::calculateEnvironmentHash()) + ->setEnvInfo(PhabricatorEnv::calculateEnvironmentInfo()) ->setStatus(PhabricatorDaemonLog::STATUS_RUNNING) ->setArgv($event->getValue('argv')) ->setExplicitArgv($event->getValue('explicitArgv')) diff --git a/src/applications/daemon/storage/PhabricatorDaemonLog.php b/src/applications/daemon/storage/PhabricatorDaemonLog.php index a0d121b393..0e4f1ea838 100644 --- a/src/applications/daemon/storage/PhabricatorDaemonLog.php +++ b/src/applications/daemon/storage/PhabricatorDaemonLog.php @@ -17,6 +17,7 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO protected $argv; protected $explicitArgv = array(); protected $envHash; + protected $envInfo; protected $status; protected function getConfiguration() { @@ -24,6 +25,7 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO self::CONFIG_SERIALIZATION => array( 'argv' => self::SERIALIZATION_JSON, 'explicitArgv' => self::SERIALIZATION_JSON, + 'envInfo' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'daemon' => 'text255', diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index 7e266ef469..ac733a909d 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -240,19 +240,105 @@ final class PhabricatorEnv { } public static function calculateEnvironmentHash() { - $keys = array_keys(self::getAllConfigKeys()); - sort($keys); - - $skip_keys = self::getEnvConfig('phd.variant-config'); - $keys = array_diff($keys, $skip_keys); + $keys = self::getKeysForConsistencyCheck(); $values = array(); foreach ($keys as $key) { $values[$key] = self::getEnvConfigIfExists($key); } + return PhabricatorHash::digest(json_encode($values)); } + /** + * Returns a summary of non-default configuration settings to allow the + * "daemons and web have different config" setup check to list divergent + * keys. + */ + public static function calculateEnvironmentInfo() { + $keys = self::getKeysForConsistencyCheck(); + + $info = array(); + + $defaults = id(new PhabricatorConfigDefaultSource())->getAllKeys(); + foreach ($keys as $key) { + $current = self::getEnvConfigIfExists($key); + $default = idx($defaults, $key, null); + if ($current !== $default) { + $info[$key] = PhabricatorHash::digestForIndex(json_encode($current)); + } + } + + $keys_hash = array_keys($defaults); + sort($keys_hash); + $keys_hash = implode("\0", $keys_hash); + $keys_hash = PhabricatorHash::digestForIndex($keys_hash); + + return array( + 'version' => 1, + 'keys' => $keys_hash, + 'values' => $info, + ); + } + + + /** + * Compare two environment info summaries to generate a human-readable + * list of discrepancies. + */ + public static function compareEnvironmentInfo(array $u, array $v) { + $issues = array(); + + $uversion = idx($u, 'version'); + $vversion = idx($v, 'version'); + if ($uversion != $vversion) { + $issues[] = pht( + 'The two configurations were generated by different versions '. + 'of Phabricator.'); + + // These may not be comparable, so stop here. + return $issues; + } + + if ($u['keys'] !== $v['keys']) { + $issues[] = pht( + 'The two configurations have different keys. This usually means '. + 'that they are running different versions of Phabricator.'); + } + + $uval = idx($u, 'values', array()); + $vval = idx($v, 'values', array()); + + $all_keys = array_keys($uval + $vval); + + foreach ($all_keys as $key) { + $uv = idx($uval, $key); + $vv = idx($vval, $key); + if ($uv !== $vv) { + if ($uv && $vv) { + $issues[] = pht( + 'The configuration key "%s" is set in both configurations, but '. + 'set to different values.', + $key); + } else { + $issues[] = pht( + 'The configuration key "%s" is set in only one configuration.', + $key); + } + } + } + + return $issues; + } + + private static function getKeysForConsistencyCheck() { + $keys = array_keys(self::getAllConfigKeys()); + sort($keys); + + $skip_keys = self::getEnvConfig('phd.variant-config'); + return array_diff($keys, $skip_keys); + } + /* -( Reading Configuration )---------------------------------------------- */ diff --git a/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php index 59823fdbed..086b487f1f 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php @@ -906,6 +906,11 @@ abstract class PhabricatorBaseEnglishTranslation 'You have an unpaid invoice.', 'You have unpaid invoices.', ), + + 'The configurations differ in the following %s way(s):' => array( + 'The configurations differ:', + 'The configurations differ in these ways:', + ), ); }