PowerShellでSubversionの引っ越しをした話

こんにちは、AWS担当のwakです。表題の通りのダーティーな仕事をしたのでメモを書いておきます。

f:id:nurenezumi:20150617193841j:plain

ダーティーではないきれいな猫

Subversionは死なず(死んでくれない)

弊社社内でも新規プロジェクトではGitを利用しているのですが、何年も前から開発が続いているアプリケーションではブランチが大量にあったりするためになかなか移行ができません。また、ある箇所がなぜ・どのように修正されたかの経緯を知ることが必要になるケースが稀に発生します。

AWSに引っ越そう

ところで、弊社では社内のサーバーを原則AWSへ移行する計画があり、現在その作業の真っ最中です。ADサーバー、ファイルサーバーも対象ですから、むろん現行のSVNサーバーも例外ではありません。そこで、まず話が早そうなSVNサーバーから引っ越すことにしてみました。

作業時の制約事項

ネットワークや帯域、ユーザー権限などの事情が絡み、次のような(変な)制約事項がありました。

  • 事前準備に時間は使えますが、切り替えに時間はかけられません。
    • 使えない時間帯が何時間も発生するのは避けたいです。
  • 作業は作業用サーバー(Windows Server 2008)で行います。
    • 作業用サーバーはポンコツなので、ディスクの空き容量はほとんどありません。下手にhotcopyを実行しようとするとあふれます。
    • 作業用サーバーに何かをインストールすることはできません。実行ファイルを持ち込むことならできます。
  • 移行先のEC2とはまだVPNが開通していません。インターネット上に浮かんでいます。

やったこと

概要

このようなループをぐるぐる回します。

  1. 対象のリポジトリのリビジョン1~100をダンプしてファイルへ落とします。
  2. 1のファイルをS3にアップロードします。
  3. 1のファイルを削除します。
  4. リビジョン101~200まで、201~300まで、301~400まで……について1から3までの手順を繰り返し、リポジトリの内容をすべてS3にアップロードします。

次に移行先のEC2に移動してこのダンプを取り込みます。

  1. S3からファイルを全て取得します。
  2. リビジョン番号ごとにファイルをソートします。
  3. 順にリポジトリへ流し込みます。

これで現時点でのリポジトリの中身を履歴も含めて全てEC2に持っていきます。半日ぐらいかかりましたが、作業中であっても現行のリポジトリはそのまま利用できます。最後に移行当日に残った差分だけを持っていけば、切り替えはすぐに終わるという計画です。

出力スクリプト

次のようなPowerShellスクリプトを実行しました。本当は7zipで圧縮するつもりだったのですがサボっています。

# 他のPCから持ち込んだAWS Tools for PowerShellのDLLをインポート
Import-Module .\AWSPowerShell.dll
# 認証を先に通しておく
Initialize-AWSDefaults -accessKey "YOUR-ACCESS-KEY" -secretKey "YOUR-SECRET-KEY" -Region ap-northeast-1

# ここからが本番
$repoName = "YourRepositoryName"   # ダンプする対象のリポジトリの名前
$bucketName = "YourBucketName" # S3のバケット名
$repoLoc = "Path-To-Your-Repositories\" + $repoName # リポジトリへのフルパス
$s3Dir = "dir1/dir2" # 保存するS3のパス(先頭・末尾のスラッシュは不要)

$start = 1 # ダンプする最初のリビジョン
$step = 100 # 1回のダンプでいくつ分のリビジョンを出力するか
$loop = 256 # 何回ループするか
for ($i = 0; $i -lt $loop; $i++) {
  $lower = $i * $step + $start
  $upper = $lower + $step - 1
  $rev = "{0}:{1}" -f $lower, $upper
  $filename = "{0}_{1:D4}_{2:D4}.dump" -f $repoName, $lower, $upper
  &cmd /c "svnadmin dump $repoLoc --incremental -r $rev > $filename"
  Write-S3Object -BucketName $bucketName -File $filename -Key "$s3Dir/$repoName/$filename"
  Remove-Item $filename
}

取り込みスクリプト

まずファイルをダウンロードしましょう。こちらはEC2で動かす想定なのでPowerShellはそのまま使えます。

Initialize-AWSDefaults -accessKey "YOUR-ACCESS-KEY" -secretKey "YOUR-SECRET-KEY" -Region ap-northeast-1
Get-S3Object -BucketName YourBucketName -KeyPrefix "dir1/dir2/YourRepositoryName/" | % {
  $s = $_.Key.Split("/")
  $fn = $s[$s.Length - 1]
  Read-S3Object -BucketName backup-sanwasystem -Key $_.Key -File $fn
}

ファイルがダウンロードできたら空のリポジトリを作ります。ディレクトリを手で作成し、svnadmin create YourRepositoryName でできます。最後にsvnadmin loadコマンドで投入するだけです。

dir *.dump | Sort-Object Name | % {
  &cmd /c ("svnadmin load Path-To-Your-Repositories\YourRepositoryName < " + $_.Name)
}

出力時に必要なもの

自分のPC・他サーバーから次のファイルを持ち込みました。これがないとエラーになります。

  • C:\Program Files (x86)\AWS Tools\PowerShell\AWSPowerShell 以下のファイル。Write-S3Objectコマンドレットを実行するために必要です。AWSのサイトから AWS Tools for Windows PowerShell をインストールすると入ります。
  • VisualSVN がインストール済みの別サーバーから持ち出したコマンドラインツール。svnadmin.exe, svnlook.exe が必要でした。

また、S3を読み書きできるIAMユーザーとそのアクセスキーも必要です。あらかじめ作成しておきましょう。

ポイント

なんということはないスクリプトですが、気をつけるのはこんなところです。

  • PowerShellは標準出力をすべてUTF-16に変換してしまいます。そのため、PowerShellから直接svnadminコマンドを実行して結果をリダイレクトでファイルに流し込もうとすると失敗します。cmd.exeを使っているのはそのためです。
  • 取り込み時はファイルをリビジョンの順番通り取り込む処理する必要がありますが、reponame_1_100.dump, reponame_101_200.dump, ... のような名前だとソートが面倒です。リビジョン番号は0埋めして数字の桁数を揃えておきましょう。

まとめ(?)

こんな方法もあるよ、というお話でした。PowerShellが使えなかったらもう少し面倒なことになっていたような気がします。