Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 50
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 */
20// @codeCoverageIgnoreStart
21if ( getenv( 'MW_INSTALL_PATH' ) ) {
22    $IP = getenv( 'MW_INSTALL_PATH' );
23} else {
24    $IP = __DIR__ . '/../../..';
25}
26
27require_once "$IP/maintenance/Maintenance.php";
28// @codeCoverageIgnoreEnd
29
30use MediaWiki\Extension\OAuth\Backend\Consumer;
31use MediaWiki\Extension\OAuth\Backend\ConsumerAcceptance;
32use MediaWiki\Extension\OAuth\Backend\MWOAuthDAO;
33use MediaWiki\Maintenance\Maintenance;
34use MediaWiki\MediaWikiServices;
35
36class MigrateCentralWiki extends Maintenance {
37    public function __construct() {
38        parent::__construct();
39        $this->addDescription( "Migrate central wiki from one wiki to another. " .
40            "OAuth should be in Read Only mode while this is running." );
41        $this->addOption( 'old', 'Previous central wiki', true, true );
42        $this->addOption( 'target', 'New central wiki', true, true );
43        $this->addOption( 'table',
44            'Table name (oauth_registered_consumer or oauth_accepted_consumer)', true, true );
45        $this->setBatchSize( 200 );
46        $this->requireExtension( "OAuth" );
47    }
48
49    public function execute() {
50        $oldWiki = $this->getOption( 'old' );
51        $targetWiki = $this->getOption( 'target' );
52        $table = $this->getOption( 'table' );
53
54        if ( $table === 'oauth_registered_consumer' ) {
55            $idKey = 'oarc_id';
56            $cmrClass = Consumer::class;
57            $type = 'consumer';
58        } elseif ( $table === 'oauth_accepted_consumer' ) {
59            $idKey = 'oaac_id';
60            $cmrClass = ConsumerAcceptance::class;
61            $type = 'grant';
62        } else {
63            $this->fatalError( "Invalid table name. Must be one of 'oauth_registered_consumer' " .
64                "or 'oauth_accepted_consumer'.\n" );
65        }
66
67        $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
68        $oldDb = $lbFactory->getMainLB( $oldWiki )->getConnection( DB_PRIMARY, [], $oldWiki );
69        $targetDb = $lbFactory->getMainLB( $targetWiki )
70            ->getConnection( DB_PRIMARY, [], $targetWiki );
71
72        $newMax = $targetDb->newSelectQueryBuilder()
73            ->select( "MAX($idKey)" )
74            ->from( $table )
75            ->caller( __METHOD__ )
76            ->fetchField();
77
78        $oldMax = $oldDb->newSelectQueryBuilder()
79            ->select( "MAX($idKey)" )
80            ->from( $table )
81            ->caller( __METHOD__ )
82            ->fetchField();
83
84        if ( $newMax >= $oldMax ) {
85            $this->output( "No new rows.\n" );
86        }
87
88        $lbFactory = MediaWikiServices::getInstance()->getDBLoadBalancerFactory();
89
90        for ( $currentId = $newMax + 1, $i = 1; $currentId <= $oldMax; ++$currentId, ++$i ) {
91            $this->output( "Migrating $type $currentId..." );
92            /** @var MWOAuthDAO $cmrClass */
93            $cmr = $cmrClass::newFromId( $oldDb, $currentId );
94            if ( $cmr ) {
95                $cmr->updateOrigin( 'new' );
96                $cmr->setPending( true );
97                $cmr->save( $targetDb );
98                $this->output( "done.\n" );
99            } else {
100                $this->output( "missing.\n" );
101            }
102
103            if ( $this->mBatchSize && $i % $this->mBatchSize === 0 ) {
104                $lbFactory->waitForReplication( [ 'domain' => $targetWiki ] );
105            }
106        }
107    }
108
109}
110
111// @codeCoverageIgnoreStart
112$maintClass = MigrateCentralWiki::class;
113require_once RUN_MAINTENANCE_IF_MAIN;
114// @codeCoverageIgnoreEnd