Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 56
0.00% covered (danger)
0.00%
0 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
MigrateCentralWiki
0.00% covered (danger)
0.00%
0 / 50
0.00% covered (danger)
0.00%
0 / 2
90
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 execute
0.00% covered (danger)
0.00%
0 / 41
0.00% covered (danger)
0.00%
0 / 1
72
1<?php
2
3namespace MediaWiki\Extension\OAuth;
4
5/**
6 * Migrate oauth_registered_consumer and oauth_accepted_consumer tables to a
7 * new database with minimal downtime. This script assumes relatively small tables
8 * (The WMF has <100 consumers and aout 1000 authorizations right now).
9 *
10 * To migrate to a new central wiki within your cluster, you roughly want to:
11 * 1. Set $wgMWOAuthReadOnly = true for all wikis in your running config
12 * 2. Move the oauth_registered_consumer and oauth_accepted_consumer tables with this script
13 * 3. Update the cluster config to point to the new central wiki
14 * 4. Set $wgMWOAuthReadOnly back to false, so users can manage their consumers as normal.
15 * 5. Migrate the OAuth logs using importCentralWikiLogs.php.
16 * 6. Done!
17 *
18 * @ingroup Maintenance
19 */
20if ( getenv( 'MW_INSTALL_PATH' ) ) {
21    $IP = getenv( 'MW_INSTALL_PATH' );
22} else {
23    $IP = __DIR__ . '/../../..';
24}
25
26require_once "$IP/maintenance/Maintenance.php";
27
28use MediaWiki\Extension\OAuth\Backend\Consumer;
29use MediaWiki\Extension\OAuth\Backend\ConsumerAcceptance;
30use MediaWiki\Extension\OAuth\Backend\MWOAuthDAO;
31use MediaWiki\Maintenance\Maintenance;
32use MediaWiki\MediaWikiServices;
33
34class MigrateCentralWiki extends Maintenance {
35    public function __construct() {
36        parent::__construct();
37        $this->addDescription( "Migrate central wiki from one wiki to another. " .
38            "OAuth should be in Read Only mode while this is running." );
39        $this->addOption( 'old', 'Previous central wiki', true, true );
40        $this->addOption( 'target', 'New central wiki', true, true );
41        $this->addOption( 'table',
42            'Table name (oauth_registered_consumer or oauth_accepted_consumer)', true, true );
43        $this->setBatchSize( 200 );
44        $this->requireExtension( "OAuth" );
45    }
46
47    public function execute() {
48        $oldWiki = $this->getOption( 'old' );
49        $targetWiki = $this->getOption( 'target' );
50        $table = $this->getOption( 'table' );
51
52        if ( $table === 'oauth_registered_consumer' ) {
53            $idKey = 'oarc_id';
54            $cmrClass = Consumer::class;
55            $type = 'consumer';
56        } elseif ( $table === 'oauth_accepted_consumer' ) {
57            $idKey = 'oaac_id';
58            $cmrClass = ConsumerAcceptance::class;
59            $type = 'grant';
60        } else {
61            $this->fatalError( "Invalid table name. Must be one of 'oauth_registered_consumer' " .
62                "or 'oauth_accepted_consumer'.\n" );
63        }
64
65        $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
66        $oldDb = $lbFactory->getMainLB( $oldWiki )->getConnection( DB_PRIMARY, [], $oldWiki );
67        $targetDb = $lbFactory->getMainLB( $targetWiki )
68            ->getConnection( DB_PRIMARY, [], $targetWiki );
69
70        $newMax = $targetDb->newSelectQueryBuilder()
71            ->select( "MAX($idKey)" )
72            ->from( $table )
73            ->caller( __METHOD__ )
74            ->fetchField();
75
76        $oldMax = $oldDb->newSelectQueryBuilder()
77            ->select( "MAX($idKey)" )
78            ->from( $table )
79            ->caller( __METHOD__ )
80            ->fetchField();
81
82        if ( $newMax >= $oldMax ) {
83            $this->output( "No new rows.\n" );
84        }
85
86        $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
87
88        for ( $currentId = $newMax + 1, $i = 1; $currentId <= $oldMax; ++$currentId, ++$i ) {
89            $this->output( "Migrating $type $currentId..." );
90            /** @var MWOAuthDAO $cmrClass */
91            $cmr = $cmrClass::newFromId( $oldDb, $currentId );
92            if ( $cmr ) {
93                $cmr->updateOrigin( 'new' );
94                $cmr->setPending( true );
95                $cmr->save( $targetDb );
96                $this->output( "done.\n" );
97            } else {
98                $this->output( "missing.\n" );
99            }
100
101            if ( $this->mBatchSize && $i % $this->mBatchSize === 0 ) {
102                $lbFactory->waitForReplication( [ 'domain' => $targetWiki ] );
103            }
104        }
105    }
106
107}
108
109$maintClass = MigrateCentralWiki::class;
110require_once RUN_MAINTENANCE_IF_MAIN;