Allow some maintenance scripts to be called without a LocalSettings
Subclasses of Maintenance that can function without database access can now override canExecuteWithoutLocalSettings to return true. This is useful for scripts that need to be able to run before or without installing MediaWiki. When no config file is present, the storage backend will be disabled. Any attempt to access the database will result in an error. NOTE: This makes MediaWikiServices::disableBackendServices() more comprehensive, to avoid premature failures during service construction. This change makes it necessary to adjust how the installer manages service overrides. The CLI installer is covered by CI, I tested the web installer manually. Change-Id: Ie84010c80f32cbdfd34aff9dde1bfde1ed531793
This commit is contained in:
parent
fd660e4c51
commit
7dcfbf2a62
13 changed files with 393 additions and 89 deletions
|
|
@ -2827,6 +2827,7 @@ $wgAutoloadLocalClasses = [
|
|||
'Wikimedia\\Rdbms\\LBFactorySingle' => __DIR__ . '/includes/libs/rdbms/lbfactory/LBFactorySingle.php',
|
||||
'Wikimedia\\Rdbms\\LikeMatch' => __DIR__ . '/includes/libs/rdbms/encasing/LikeMatch.php',
|
||||
'Wikimedia\\Rdbms\\LoadBalancer' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancer.php',
|
||||
'Wikimedia\\Rdbms\\LoadBalancerDisabled' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancerDisabled.php',
|
||||
'Wikimedia\\Rdbms\\LoadBalancerSingle' => __DIR__ . '/includes/libs/rdbms/loadbalancer/LoadBalancerSingle.php',
|
||||
'Wikimedia\\Rdbms\\LoadMonitor' => __DIR__ . '/includes/libs/rdbms/loadmonitor/LoadMonitor.php',
|
||||
'Wikimedia\\Rdbms\\LoadMonitorNull' => __DIR__ . '/includes/libs/rdbms/loadmonitor/LoadMonitorNull.php',
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ use MediaWiki\HookContainer\HookContainer;
|
|||
use MediaWiki\HookContainer\HookRunner;
|
||||
use MediaWiki\Http\HttpRequestFactory;
|
||||
use MediaWiki\Interwiki\InterwikiLookup;
|
||||
use MediaWiki\Interwiki\NullInterwikiLookup;
|
||||
use MediaWiki\JobQueue\JobFactory;
|
||||
use MediaWiki\JobQueue\JobQueueGroupFactory;
|
||||
use MediaWiki\Json\JsonCodec;
|
||||
|
|
@ -198,6 +199,8 @@ use Wikimedia\Parsoid\Config\SiteConfig;
|
|||
use Wikimedia\Rdbms\DatabaseFactory;
|
||||
use Wikimedia\Rdbms\ILoadBalancer;
|
||||
use Wikimedia\Rdbms\LBFactory;
|
||||
use Wikimedia\Rdbms\LBFactorySingle;
|
||||
use Wikimedia\Rdbms\LoadBalancerDisabled;
|
||||
use Wikimedia\RequestTimeout\CriticalSectionProvider;
|
||||
use Wikimedia\Services\NoSuchServiceException;
|
||||
use Wikimedia\Services\SalvageableService;
|
||||
|
|
@ -228,6 +231,12 @@ class MediaWikiServices extends ServiceContainer {
|
|||
*/
|
||||
private static $instance = null;
|
||||
|
||||
/**
|
||||
* @see disableStorage()
|
||||
* @var bool
|
||||
*/
|
||||
private bool $storageDisabled = false;
|
||||
|
||||
/**
|
||||
* Allows a global service container instance to exist.
|
||||
*
|
||||
|
|
@ -448,12 +457,29 @@ class MediaWikiServices extends ServiceContainer {
|
|||
return $instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables all storage layer services. After calling this, any attempt to access the
|
||||
* storage layer will result in an error.
|
||||
*
|
||||
* @since 1.28
|
||||
* @deprecated since 1.40, use disableStorage() instead.
|
||||
*
|
||||
* @warning This is intended for extreme situations, see the documentation of disableStorage() for details.
|
||||
*
|
||||
* @see resetGlobalInstance()
|
||||
* @see resetChildProcessServices()
|
||||
*/
|
||||
public static function disableStorageBackend() {
|
||||
$services = self::getInstance();
|
||||
$services->disableStorage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables all storage layer services. After calling this, any attempt to access the
|
||||
* storage layer will result in an error. Use resetGlobalInstance() to restore normal
|
||||
* operation.
|
||||
*
|
||||
* @since 1.28
|
||||
* @since 1.40
|
||||
*
|
||||
* @warning This is intended for extreme situations only and should never be used
|
||||
* while serving normal web requests. Legitimate use cases for this method include
|
||||
|
|
@ -463,20 +489,69 @@ class MediaWikiServices extends ServiceContainer {
|
|||
* @see resetGlobalInstance()
|
||||
* @see resetChildProcessServices()
|
||||
*/
|
||||
public static function disableStorageBackend() {
|
||||
// TODO: also disable some Caches, JobQueues, etc
|
||||
$destroy = [ 'DBLoadBalancer', 'DBLoadBalancerFactory' ];
|
||||
$services = self::getInstance();
|
||||
|
||||
foreach ( $destroy as $name ) {
|
||||
$services->disableService( $name );
|
||||
public function disableStorage() {
|
||||
if ( $this->storageDisabled ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->redefineService(
|
||||
'DBLoadBalancer',
|
||||
static function ( MediaWikiServices $services ) {
|
||||
return new LoadBalancerDisabled();
|
||||
}
|
||||
);
|
||||
|
||||
$this->redefineService(
|
||||
'DBLoadBalancerFactory',
|
||||
static function ( MediaWikiServices $services ) {
|
||||
return LBFactorySingle::newDisabled();
|
||||
}
|
||||
);
|
||||
|
||||
$this->redefineService(
|
||||
'InterwikiLookup',
|
||||
static function ( MediaWikiServices $services ) {
|
||||
return new NullInterwikiLookup();
|
||||
}
|
||||
);
|
||||
|
||||
$this->redefineService(
|
||||
'UserOptionsLookup',
|
||||
static function ( MediaWikiServices $services ) {
|
||||
return $services->get( '_DefaultOptionsLookup' );
|
||||
}
|
||||
);
|
||||
|
||||
$this->addServiceManipulator(
|
||||
'LocalisationCache',
|
||||
static function ( LocalisationCache $cache ) {
|
||||
$cache->disableBackend();
|
||||
}
|
||||
);
|
||||
|
||||
$this->addServiceManipulator(
|
||||
'MessageCache',
|
||||
static function ( MessageCache $cache ) {
|
||||
$cache->disable();
|
||||
}
|
||||
);
|
||||
|
||||
ObjectCache::clear();
|
||||
|
||||
$this->storageDisabled = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets any services that may have become stale after a child process
|
||||
* Returns true if disableStorage() has been called on this MediaWikiServices instance.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isStorageDisabled(): bool {
|
||||
return $this->storageDisabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets any services that may have become stale after a child processö
|
||||
* returns from after pcntl_fork(). It's also safe, but generally unnecessary,
|
||||
* to call this method from the parent process.
|
||||
*
|
||||
|
|
@ -485,7 +560,7 @@ class MediaWikiServices extends ServiceContainer {
|
|||
* @note This is intended for use in the context of process forking only!
|
||||
*
|
||||
* @see resetGlobalInstance()
|
||||
* @see disableStorageBackend()
|
||||
* @see disableStorage()
|
||||
*/
|
||||
public static function resetChildProcessServices() {
|
||||
// NOTE: for now, just reset everything. Since we don't know the interdependencies
|
||||
|
|
@ -547,7 +622,7 @@ class MediaWikiServices extends ServiceContainer {
|
|||
*
|
||||
* @see resetGlobalInstance()
|
||||
* @see forceGlobalInstance()
|
||||
* @see disableStorageBackend()
|
||||
* @see disableStorage()
|
||||
*/
|
||||
public static function failIfResetNotAllowed( $method ) {
|
||||
if ( !defined( 'MW_PHPUNIT_TEST' )
|
||||
|
|
|
|||
|
|
@ -413,10 +413,8 @@ abstract class Installer {
|
|||
$defaultConfig = new GlobalVarConfig(); // all the defaults from config-schema.yaml.
|
||||
$installerConfig = self::getInstallerConfig( $defaultConfig );
|
||||
|
||||
$this->resetMediaWikiServices( $installerConfig );
|
||||
|
||||
// Disable all storage services, since we don't have any configuration yet!
|
||||
MediaWikiServices::disableStorageBackend();
|
||||
$this->resetMediaWikiServices( $installerConfig, [], true );
|
||||
|
||||
$this->settings = $this->getDefaultSettings();
|
||||
|
||||
|
|
@ -465,31 +463,48 @@ abstract class Installer {
|
|||
* @param array $serviceOverrides Service definition overrides. Values can be null to
|
||||
* disable specific overrides that would be applied per default, namely
|
||||
* 'InterwikiLookup' and 'UserOptionsLookup'.
|
||||
* @param bool $disableStorage Whether MediaWikiServices::disableStorage() should be called.
|
||||
*
|
||||
* @return MediaWikiServices
|
||||
*/
|
||||
public function resetMediaWikiServices( Config $installerConfig = null, $serviceOverrides = [] ) {
|
||||
public function resetMediaWikiServices(
|
||||
Config $installerConfig = null,
|
||||
$serviceOverrides = [],
|
||||
bool $disableStorage = false
|
||||
) {
|
||||
global $wgObjectCaches, $wgLang;
|
||||
|
||||
$serviceOverrides += [
|
||||
// Disable interwiki lookup, to avoid database access during parses
|
||||
'InterwikiLookup' => static function () {
|
||||
return new NullInterwikiLookup();
|
||||
},
|
||||
|
||||
// Disable user options database fetching, only rely on default options.
|
||||
'UserOptionsLookup' => static function ( MediaWikiServices $services ) {
|
||||
return $services->get( '_DefaultOptionsLookup' );
|
||||
}
|
||||
];
|
||||
|
||||
$lang = $this->getVar( '_UserLang', 'en' );
|
||||
|
||||
// Reset all services and inject config overrides
|
||||
// Reset all services and inject config overrides.
|
||||
// NOTE: This will reset existing instances, but not previous wiring overrides!
|
||||
MediaWikiServices::resetGlobalInstance( $installerConfig );
|
||||
|
||||
$mwServices = MediaWikiServices::getInstance();
|
||||
|
||||
if ( $disableStorage ) {
|
||||
$mwServices->disableStorage();
|
||||
} else {
|
||||
// Default to partially disabling services.
|
||||
|
||||
$serviceOverrides += [
|
||||
// Disable interwiki lookup, to avoid database access during parses
|
||||
'InterwikiLookup' => static function () {
|
||||
return new NullInterwikiLookup();
|
||||
},
|
||||
|
||||
// Disable user options database fetching, only rely on default options.
|
||||
'UserOptionsLookup' => static function ( MediaWikiServices $services ) {
|
||||
return $services->get( '_DefaultOptionsLookup' );
|
||||
},
|
||||
|
||||
// Restore to default wiring, in case it was overwritten by disableStorage()
|
||||
'DBLoadBalancer' => static function ( MediaWikiServices $services ) {
|
||||
return $services->getDBLoadBalancerFactory()->getMainLB();
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
$lang = $this->getVar( '_UserLang', 'en' );
|
||||
|
||||
foreach ( $serviceOverrides as $name => $callback ) {
|
||||
// Skip if the caller set $callback to null
|
||||
// to suppress default overrides.
|
||||
|
|
|
|||
|
|
@ -34,20 +34,26 @@ class LBFactorySingle extends LBFactory {
|
|||
/**
|
||||
* You probably want to use {@link newFromConnection} instead.
|
||||
*
|
||||
* @param array $conf An associative array with one member:
|
||||
* - connection: The IDatabase connection object
|
||||
* @param array $conf An associative array containing one of the following:
|
||||
* - connection: The IDatabase connection object to use
|
||||
* - lb: The ILoadBalancer object to use
|
||||
*/
|
||||
public function __construct( array $conf ) {
|
||||
parent::__construct( $conf );
|
||||
|
||||
if ( !isset( $conf['connection'] ) ) {
|
||||
throw new InvalidArgumentException( "Missing 'connection' argument." );
|
||||
if ( isset( $conf['lb'] ) ) {
|
||||
$lb = $conf['lb'];
|
||||
} else {
|
||||
if ( !isset( $conf['connection'] ) ) {
|
||||
throw new InvalidArgumentException( "Missing 'connection' argument." );
|
||||
}
|
||||
|
||||
$lb = new LoadBalancerSingle( array_merge(
|
||||
$this->baseLoadBalancerParams(),
|
||||
$conf
|
||||
) );
|
||||
}
|
||||
|
||||
$lb = new LoadBalancerSingle( array_merge(
|
||||
$this->baseLoadBalancerParams(),
|
||||
$conf
|
||||
) );
|
||||
$this->initLoadBalancer( $lb );
|
||||
|
||||
$this->lb = $lb;
|
||||
|
|
@ -67,6 +73,19 @@ class LBFactorySingle extends LBFactory {
|
|||
) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $params Parameter map to LBFactorySingle::__construct()
|
||||
* and LoadBalancerDisabled::__construct()
|
||||
* @return LBFactorySingle
|
||||
* @since 1.40
|
||||
*/
|
||||
public static function newDisabled( array $params = [] ) {
|
||||
return new static( array_merge(
|
||||
[ 'lb' => new LoadBalancerDisabled( $params ) ],
|
||||
$params
|
||||
) );
|
||||
}
|
||||
|
||||
public function newMainLB( $domain = false ): ILoadBalancerForOwner {
|
||||
// @phan-suppress-previous-line PhanPluginNeverReturnMethod
|
||||
throw new BadMethodCallException( "Method is not supported." );
|
||||
|
|
|
|||
115
includes/libs/rdbms/loadbalancer/LoadBalancerDisabled.php
Normal file
115
includes/libs/rdbms/loadbalancer/LoadBalancerDisabled.php
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
<?php
|
||||
/**
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* @file
|
||||
*/
|
||||
namespace Wikimedia\Rdbms;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Trivial LoadBalancer that always fails.
|
||||
* Useful when running code with no config file present,
|
||||
* e.g. during the installation process.
|
||||
*
|
||||
* @since 1.40
|
||||
*
|
||||
* @ingroup Database
|
||||
*/
|
||||
class LoadBalancerDisabled extends LoadBalancer {
|
||||
|
||||
public function __construct( $params = [] ) {
|
||||
parent::__construct( [
|
||||
'servers' => [ [
|
||||
'type' => 'disabled',
|
||||
'host' => '(disabled)',
|
||||
'dbname' => null,
|
||||
'load' => 1,
|
||||
] ],
|
||||
'trxProfiler' => $params['trxProfiler'] ?? null,
|
||||
'srvCache' => $params['srvCache'] ?? null,
|
||||
'wanCache' => $params['wanCache'] ?? null,
|
||||
'localDomain' => $params['localDomain'] ?? '(disabled)',
|
||||
'readOnlyReason' => $params['readOnlyReason'] ?? false,
|
||||
'clusterName' => $params['clusterName'] ?? null,
|
||||
] );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i
|
||||
* @param DatabaseDomain $domain
|
||||
* @param array $lbInfo
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
protected function reallyOpenConnection( $i, DatabaseDomain $domain, array $lbInfo ) {
|
||||
throw new RuntimeException( 'Database backend disabled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i Specific (overrides $groups) or virtual (DB_PRIMARY/DB_REPLICA) server index
|
||||
* @param string[]|string $groups Query group(s) in preference order; [] for the default group
|
||||
* @param string|false $domain DB domain ID or false for the local domain
|
||||
* @param int $flags Bitfield of CONN_* class constants
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function getConnection( $i, $groups = [], $domain = false, $flags = 0 ) {
|
||||
throw new RuntimeException( 'Database backend disabled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Only to be used by DBConnRef
|
||||
* @param int $i Specific (overrides $groups) or virtual (DB_PRIMARY/DB_REPLICA) server index
|
||||
* @param string[]|string $groups Query group(s) in preference order; [] for the default group
|
||||
* @param string|false $domain DB domain ID or false for the local domain
|
||||
* @param int $flags Bitfield of CONN_* class constants (e.g. CONN_TRX_AUTOCOMMIT)
|
||||
* @return never
|
||||
*/
|
||||
public function getConnectionInternal( $i, $groups = [], $domain = false, $flags = 0 ): IDatabase {
|
||||
throw new RuntimeException( 'Database backend disabled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i Specific (overrides $groups) or virtual (DB_PRIMARY/DB_REPLICA) server index
|
||||
* @param string[]|string $groups Query group(s) in preference order; [] for the default group
|
||||
* @param string|false $domain DB domain ID or false for the local domain
|
||||
* @param int $flags Bitfield of CONN_* class constants
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function getConnectionRef( $i, $groups = [], $domain = false, $flags = 0 ): IDatabase {
|
||||
throw new RuntimeException( 'Database backend disabled' );
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $i Specific (overrides $groups) or virtual (DB_PRIMARY/DB_REPLICA) server index
|
||||
* @param string[]|string $groups Query group(s) in preference order; [] for the default group
|
||||
* @param string|false $domain DB domain ID or false for the local domain
|
||||
* @param int $flags Bitfield of CONN_* class constants
|
||||
*
|
||||
* @return never
|
||||
*/
|
||||
public function getMaintenanceConnectionRef( $i, $groups = [], $domain = false, $flags = 0 ): DBConnRef {
|
||||
throw new RuntimeException( 'Database backend disabled' );
|
||||
}
|
||||
|
||||
public function reuseConnection( IDatabase $conn ) {
|
||||
// noop
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -246,9 +246,14 @@ class ObjectCache {
|
|||
}
|
||||
}
|
||||
|
||||
if ( MediaWikiServices::getInstance()->isServiceDisabled( 'DBLoadBalancer' ) ) {
|
||||
// The LoadBalancer is disabled, probably because
|
||||
// MediaWikiServices::disableStorageBackend was called.
|
||||
$services = MediaWikiServices::getInstance();
|
||||
|
||||
if ( $services->isServiceDisabled( 'DBLoadBalancer' ) ) {
|
||||
// The DBLoadBalancer service is disabled, so we can't use the database!
|
||||
$candidate = CACHE_NONE;
|
||||
} elseif ( $services->isStorageDisabled() ) {
|
||||
// Storage services are disabled because MediaWikiServices::disableStorage()
|
||||
// was called. This is typically the case during installation.
|
||||
$candidate = CACHE_NONE;
|
||||
} else {
|
||||
$candidate = CACHE_DB;
|
||||
|
|
|
|||
|
|
@ -226,6 +226,26 @@ abstract class Maintenance {
|
|||
*/
|
||||
abstract public function execute();
|
||||
|
||||
/**
|
||||
* Whether this script can run without LocalSettings.php. Scripts that need to be able
|
||||
* to run when MediaWiki has not been installed should override this to return true.
|
||||
* Scripts that return true from this method must be able to function without
|
||||
* a storage backend. When no LocalSettings.php file is present, any attempt to access
|
||||
* the database will fail with a fatal error.
|
||||
*
|
||||
* @note Subclasses that override this method to return true should also override
|
||||
* getDbType() to return self::DB_NONE, unless they are going to use the database
|
||||
* connection when it is available.
|
||||
*
|
||||
* @see getDbType()
|
||||
* @since 1.40
|
||||
* @stable to override
|
||||
* @return bool
|
||||
*/
|
||||
public function canExecuteWithoutLocalSettings(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a particular option in supported. Normally this means it
|
||||
* has been registered by the script via addOption.
|
||||
|
|
@ -554,6 +574,12 @@ abstract class Maintenance {
|
|||
* Maintenance::DB_NONE - For no DB access at all
|
||||
* Maintenance::DB_STD - For normal DB access, default
|
||||
* Maintenance::DB_ADMIN - For admin DB access
|
||||
*
|
||||
* @note Subclasses that override this method to return self::DB_NONE should
|
||||
* also override canExecuteWithoutLocalSettings() to return true, unless they
|
||||
* need the wiki to be set up for reasons beyond access to a database connection.
|
||||
*
|
||||
* @see canExecuteWithoutLocalSettings()
|
||||
* @stable to override
|
||||
* @return int
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ class MaintenanceRunner {
|
|||
/** @var bool */
|
||||
private $runFromWrapper = false;
|
||||
|
||||
/** @var bool */
|
||||
private bool $withoutLocalSettings = false;
|
||||
|
||||
/**
|
||||
* Default constructor. Children should call this *first* if implementing
|
||||
* their own constructors
|
||||
|
|
@ -459,38 +462,58 @@ class MaintenanceRunner {
|
|||
public function defineSettings() {
|
||||
global $wgCommandLineMode, $IP;
|
||||
|
||||
// NOTE: This doesn't actually load anything, that will be done later
|
||||
// by Setup.php. But it defines MW_CONFIG_CALLBACK and possibly
|
||||
// other constants that control initialization.
|
||||
if ( !defined( 'MW_CONFIG_CALLBACK' ) ) {
|
||||
if ( $this->parameters->hasOption( 'conf' ) ) {
|
||||
// Define the constant instead of directly setting $settingsFile
|
||||
// to ensure consistency. wfDetectLocalSettingsFile() will return
|
||||
// MW_CONFIG_FILE if it is defined.
|
||||
define( 'MW_CONFIG_FILE', $this->parameters->getOption( 'conf' ) );
|
||||
}
|
||||
$settingsFile = wfDetectLocalSettingsFile( $IP );
|
||||
if ( $this->parameters->hasOption( 'conf' ) ) {
|
||||
// Define the constant instead of directly setting $settingsFile
|
||||
// to ensure consistency. wfDetectLocalSettingsFile() will return
|
||||
// MW_CONFIG_FILE if it is defined.
|
||||
define( 'MW_CONFIG_FILE', $this->parameters->getOption( 'conf' ) );
|
||||
|
||||
if ( $this->parameters->hasOption( 'wiki' ) ) {
|
||||
$wikiName = $this->parameters->getOption( 'wiki' );
|
||||
$bits = explode( '-', $wikiName, 2 );
|
||||
define( 'MW_DB', $bits[0] );
|
||||
define( 'MW_PREFIX', $bits[1] ?? '' );
|
||||
define( 'MW_WIKI_NAME', $wikiName );
|
||||
} elseif ( $this->parameters->hasOption( 'server' ) ) {
|
||||
// Provide the option for site admins to detect and configure
|
||||
// multiple wikis based on server names. This offers --server
|
||||
// as alternative to --wiki.
|
||||
// See https://www.mediawiki.org/wiki/Manual:Wiki_family
|
||||
$_SERVER['SERVER_NAME'] = $this->parameters->getOption( 'server' );
|
||||
if ( !is_readable( MW_CONFIG_FILE ) ) {
|
||||
$this->fatalError( "\nConfig file " . MW_CONFIG_FILE . " was not found or is not readable.\n\n" );
|
||||
}
|
||||
|
||||
if ( !is_readable( $settingsFile ) ) {
|
||||
$this->fatalError( "The file $settingsFile must exist and be readable.\n" .
|
||||
"Use --conf to specify it." );
|
||||
}
|
||||
$wgCommandLineMode = true;
|
||||
}
|
||||
$settingsFile = wfDetectLocalSettingsFile( $IP );
|
||||
|
||||
if ( $this->parameters->hasOption( 'wiki' ) ) {
|
||||
$wikiName = $this->parameters->getOption( 'wiki' );
|
||||
$bits = explode( '-', $wikiName, 2 );
|
||||
define( 'MW_DB', $bits[0] );
|
||||
define( 'MW_PREFIX', $bits[1] ?? '' );
|
||||
define( 'MW_WIKI_NAME', $wikiName );
|
||||
} elseif ( $this->parameters->hasOption( 'server' ) ) {
|
||||
// Provide the option for site admins to detect and configure
|
||||
// multiple wikis based on server names. This offers --server
|
||||
// as alternative to --wiki.
|
||||
// See https://www.mediawiki.org/wiki/Manual:Wiki_family
|
||||
$_SERVER['SERVER_NAME'] = $this->parameters->getOption( 'server' );
|
||||
}
|
||||
|
||||
if ( !is_readable( $settingsFile ) ) {
|
||||
// NOTE: Some maintenance scripts can (and need to) run without LocalSettings.
|
||||
// But we only know that once we have instantiated the Maintenance object.
|
||||
// So go into no-settings mode for now, and fail later of the script doesn't support it.
|
||||
if ( !defined( 'MW_CONFIG_CALLBACK' ) ) {
|
||||
define( 'MW_CONFIG_CALLBACK', __CLASS__ . '::emulateConfig' );
|
||||
}
|
||||
$this->withoutLocalSettings = true;
|
||||
}
|
||||
$wgCommandLineMode = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SettingsBuilder $settings
|
||||
*
|
||||
* @internal Handler for MW_CONFIG_CALLBACK, used when no LocalSettings.php was found.
|
||||
*/
|
||||
public static function emulateConfig( SettingsBuilder $settings ) {
|
||||
// NOTE: The config schema is already loaded at this point, so default values are known.
|
||||
|
||||
// Server must be set, but we don't care to what
|
||||
$settings->overrideConfigValue( 'Server', 'https://unknown.invalid' );
|
||||
|
||||
// If InvalidateCacheOnLocalSettingsChange is enabled, filemtime( MW_CONFIG_FILE ),
|
||||
// which will produce a warning if there is no settings file.
|
||||
$settings->overrideConfigValue( 'InvalidateCacheOnLocalSettingsChange', false );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -565,10 +588,18 @@ class MaintenanceRunner {
|
|||
// Double check required extensions are installed
|
||||
$this->scriptObject->checkRequiredExtensions();
|
||||
|
||||
if ( $this->scriptObject->getDbType() == Maintenance::DB_NONE ) {
|
||||
if ( $this->withoutLocalSettings && !$this->scriptObject->canExecuteWithoutLocalSettings() ) {
|
||||
$this->fatalError(
|
||||
"\nThe LocalSettings.php file was not found or is not readable.\n" .
|
||||
"Use --conf to specify an alternative config file.\n\n"
|
||||
);
|
||||
}
|
||||
|
||||
if ( $this->scriptObject->getDbType() == Maintenance::DB_NONE || $this->withoutLocalSettings ) {
|
||||
// Be strict with maintenance tasks that claim to not need a database by
|
||||
// disabling the storage backend.
|
||||
MediaWikiServices::disableStorageBackend();
|
||||
MediaWikiServices::resetGlobalInstance( $config );
|
||||
MediaWikiServices::getInstance()->disableStorage();
|
||||
}
|
||||
|
||||
$this->scriptObject->validateParamsAndArgs();
|
||||
|
|
|
|||
|
|
@ -23,11 +23,11 @@
|
|||
|
||||
// NO_AUTOLOAD -- file-scope define() used to modify behaviour
|
||||
|
||||
use MediaWiki\Settings\SettingsBuilder;
|
||||
use Wikimedia\AtEase\AtEase;
|
||||
|
||||
require_once __DIR__ . '/Maintenance.php';
|
||||
|
||||
define( 'MW_CONFIG_CALLBACK', 'Installer::overrideConfig' );
|
||||
define( 'MEDIAWIKI_INSTALL', true );
|
||||
|
||||
/**
|
||||
|
|
@ -47,8 +47,8 @@ class CommandLineInstaller extends Maintenance {
|
|||
$this->addDescription( "CLI-based MediaWiki installation and configuration.\n" .
|
||||
"Default options are indicated in parentheses." );
|
||||
|
||||
$this->addArg( 'name', 'The name of the wiki (MediaWiki)', true );
|
||||
$this->addArg( 'admin', 'The username of the wiki administrator.', true );
|
||||
$this->addArg( 'name', 'The name of the wiki' );
|
||||
$this->addArg( 'admin', 'The username of the wiki administrator.' );
|
||||
|
||||
$this->addOption( 'pass', 'The password for the wiki administrator.', false, true );
|
||||
$this->addOption(
|
||||
|
|
@ -106,6 +106,19 @@ class CommandLineInstaller extends Maintenance {
|
|||
false, true, false, true );
|
||||
}
|
||||
|
||||
public function canExecuteWithoutLocalSettings(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function finalSetup( SettingsBuilder $settingsBuilder = null ) {
|
||||
if ( !$settingsBuilder ) {
|
||||
$settingsBuilder = SettingsBuilder::getInstance();
|
||||
}
|
||||
|
||||
parent::finalSetup( $settingsBuilder );
|
||||
Installer::overrideConfig( $settingsBuilder );
|
||||
}
|
||||
|
||||
public function getDbType() {
|
||||
if ( $this->hasOption( 'env-checks' ) ) {
|
||||
return Maintenance::DB_NONE;
|
||||
|
|
@ -174,7 +187,7 @@ class CommandLineInstaller extends Maintenance {
|
|||
$passfile = $this->getOption( 'passfile' );
|
||||
if ( $passfile !== null ) {
|
||||
if ( $this->getOption( 'pass' ) !== null ) {
|
||||
$this->error( 'WARNING: You have provided the options "pass" and "passfile". '
|
||||
$this->error( 'WARNING: You have provided the option --pass or --passfile. '
|
||||
. 'The content of "passfile" overrides "pass".' );
|
||||
}
|
||||
AtEase::suppressWarnings();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ class Version extends Maintenance {
|
|||
$this->addDescription( 'Prints the current version of MediaWiki' );
|
||||
}
|
||||
|
||||
public function canExecuteWithoutLocalSettings(): bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
if ( !defined( 'MW_VERSION' ) ) {
|
||||
$this->fatalError( "MediaWiki version not defined or unknown" );
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ use MediaWiki\HookContainer\StaticHookRegistry;
|
|||
use MediaWiki\MediaWikiServices;
|
||||
use Wikimedia\Services\DestructibleService;
|
||||
use Wikimedia\Services\SalvageableService;
|
||||
use Wikimedia\Services\ServiceDisabledException;
|
||||
|
||||
/**
|
||||
* @covers MediaWiki\MediaWikiServices
|
||||
|
|
@ -185,7 +184,7 @@ class MediaWikiServicesTest extends MediaWikiIntegrationTestCase {
|
|||
MediaWikiServices::forceGlobalInstance( $oldServices );
|
||||
}
|
||||
|
||||
public function testDisableStorageBackend() {
|
||||
public function testDisableStorage() {
|
||||
$newServices = $this->newMediaWikiServices();
|
||||
$oldServices = MediaWikiServices::forceGlobalInstance( $newServices );
|
||||
|
||||
|
|
@ -198,23 +197,24 @@ class MediaWikiServicesTest extends MediaWikiIntegrationTestCase {
|
|||
}
|
||||
);
|
||||
|
||||
// force the service to become active, so we can check that it does get destroyed
|
||||
$newServices->getService( 'DBLoadBalancerFactory' );
|
||||
$this->assertFalse( $newServices->isStorageDisabled() );
|
||||
|
||||
MediaWikiServices::disableStorageBackend(); // should destroy DBLoadBalancerFactory
|
||||
$newServices->disableStorage(); // should destroy DBLoadBalancerFactory
|
||||
|
||||
$this->assertTrue( $newServices->isStorageDisabled() );
|
||||
|
||||
try {
|
||||
MediaWikiServices::getInstance()->getService( 'DBLoadBalancerFactory' );
|
||||
$this->fail( 'DBLoadBalancerFactory should have been disabled' );
|
||||
} catch ( ServiceDisabledException $ex ) {
|
||||
$newServices->getDBLoadBalancer()->getConnection( DB_REPLICA );
|
||||
} catch ( RuntimeException $ex ) {
|
||||
// ok, as expected
|
||||
} catch ( Throwable $ex ) {
|
||||
$this->fail( 'ServiceDisabledException expected, caught ' . get_class( $ex ) );
|
||||
}
|
||||
|
||||
MediaWikiServices::forceGlobalInstance( $oldServices );
|
||||
$newServices->destroy();
|
||||
|
||||
// This should work now.
|
||||
MediaWikiServices::getInstance()->getDBLoadBalancer()->getConnection( DB_REPLICA );
|
||||
|
||||
// No exception was thrown, avoid being risky
|
||||
$this->assertTrue( true );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ class FileModuleTest extends ResourceLoaderTestCase {
|
|||
$this->setService( 'SkinFactory', $skinFactory );
|
||||
|
||||
// This test is not expected to query any database
|
||||
\MediaWiki\MediaWikiServices::disableStorageBackend();
|
||||
$this->getServiceContainer()->disableStorage();
|
||||
}
|
||||
|
||||
private static function getModules() {
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ class ObjectCacheTest extends MediaWikiIntegrationTestCase {
|
|||
CACHE_ACCEL => [ 'class' => EmptyBagOStuff::class ]
|
||||
] );
|
||||
|
||||
MediaWiki\MediaWikiServices::disableStorageBackend();
|
||||
$this->getServiceContainer()->disableStorage();
|
||||
|
||||
$this->assertInstanceOf(
|
||||
EmptyBagOStuff::class,
|
||||
|
|
@ -96,7 +96,7 @@ class ObjectCacheTest extends MediaWikiIntegrationTestCase {
|
|||
|
||||
/** @covers ObjectCache::newAnything */
|
||||
public function testNewAnythingNothingNoDb() {
|
||||
MediaWiki\MediaWikiServices::disableStorageBackend();
|
||||
$this->getServiceContainer()->disableStorage();
|
||||
|
||||
$this->assertInstanceOf(
|
||||
EmptyBagOStuff::class,
|
||||
|
|
|
|||
Loading…
Reference in a new issue