ルモーリン

Google Driveに自動バックアップ

投稿:2019-07-06

引っ越し先のVPSはSSDが15GBで残り2GBを切っています。 引っ越し前は20GBだったので、その中にデーターベースのバックアップファイルを残していました。 今はバックアップファイルで空き領域を使い切ってしまいそうです。 自宅のPCにダウンロードしようか考えていると、Google Driveの空きが15GB(ほぼ全部)あることに気付きました。

みんな大好きmetacpanで検索して良さそうなのを見つけました。
Net::Google::Drive - simple Google drive API module - metacpan.org
読んでみるとアカウント以外にトークンが必要で、別のモジュールで取得できるので、これも追加します。
Net::Google::OAuth - Simple Google oauth api module - metacpan.org
これらのモジュールをcpanmでインストールすれば一発のハズでしたが、事前にData::Printerをインストールするのが必須でした。 大抵のモジュールは依存モジュールも登録されていて芋づる式にインストールされるけれど、登録漏れのようです。
Data::Printer - colored pretty-print of Perl data structures and objects - metacpan.org

大層な話ではなくて、Google Driveにアクセスするプログラムがありますよっていうのを登録してクライアントidとsecretを取得します。 一度、プログラムの権限を「Google Driveにあるデータ全てを扱える」にして登録しようとしたらGoogleの審査が必要と出てしまい、よく考えてみると このプログラムを別のアカウントでも承認すると使えてしまうものだと気付いて、「プログラムがアップロードしたデータだけ扱える」に変更して登録できました。 この場合は審査不要ですぐに登録されます(あーよかった)。

こんな感じになりました。 YAMLにクライアントidとsecretを入れて起動するとアクセスtokenとリフレッシュtokenがないので取得してYAMLに保存します。 取得の途中で表示されるURLをブラウザで開くとGoogleのページが表示されて、手作業でアプリケーションの使用を承認します。 そのあとリダイレクトされるURLをプログラムに入力するとtokenの取得が完了です。 あとはバックアップ対象のフォルダを丸ごとgzipで固めてGoogle Driveにアップロードしてくれます。

#!/usr/bin/env perl -w

use utf8;
use warnings;
use strict;
use open IO => ":utf8";

use Data::Dumper;
use DateTime;
use Encode::Argv;
use Encode::Locale;
use Fcntl qw(:flock);
use Net::Google::Drive;
use Net::Google::OAuth;
use YAML;

use FindBin;
use lib $FindBin::Bin;

use MyPrint;

binmode STDIN, ":encoding(console_in)";
binmode STDOUT, ":encoding(console_out)";

use constant EMAIL => '<メールアドレス>';
use constant PREFERENCES_FILE => "preferences.yaml";

$| = 1;

my_print "起動";

open my $single_fh, ">", "hp_backup.single";
if (!flock $single_fh, LOCK_NB | LOCK_EX) {
	my_print "同じプログラムが稼働中";
	my_print "終了";

	exit;
}

my_print "設定を読み込み";
my $preferences = YAML::LoadFile(PREFERENCES_FILE);
if (!exists $preferences->{client_id} || !exists $preferences->{client_secret}) {
	my_print "設定が足りません:" . PREFERENCES_FILE;
	exit;
}

if (!exists $preferences->{refresh_token} || !exists $preferences->{access_token}) {
	my_print "トークンを生成";
	my $ngo = Net::Google::OAuth->new(
		-client_id => $preferences->{client_id},
		-client_secret => $preferences->{client_secret},
	);

	$ngo->generateAccessToken(
	 	-scope => 'drive.file',
	 	-email => EMAIL,
	);
	$preferences->{refresh_token} = $ngo->getRefreshToken();
	$preferences->{access_token} = $ngo->getAccessToken();

	my_print "トークンを保存";
	YAML::DumpFile(PREFERENCES_FILE, $preferences);
}

my_print "アーカイブ処理";
my $hp_backup_gz = DateTime->now(time_zone => "local")->truncate(to => "day")->strftime("hp_backup_%F.gz");
my $cmd = "tar --create --gzip --file=$hp_backup_gz --directory=../myapp .";
my_print "コマンドライン:$cmd";
system($cmd);

my_print "バックアップファイル:@{[-s $hp_backup_gz]}バイト";

my $ngd = Net::Google::Drive->new(
	-client_id => $preferences->{client_id},
	-client_secret => $preferences->{client_secret},
	-refresh_token => $preferences->{refresh_token},
	-access_token => $preferences->{access_token},
);

my_print "アップロード開始";
my $res = $ngd->uploadFile(
	-source_file => $hp_backup_gz,
);

my_print "アップロード結果";
my_print Dumper $res;

unlink $hp_backup_gz;
my_print "削除";

my_print "終了";

自分で作る時はclient_idとclient_secretだけを書いて、プログラムにaccess_tokenとrefresh_tokenを取得させます。

---
client_id: <クライアントid>
client_secret: <クライアントsecret>

取得、保存したあとはこうなります。

---
access_token: <アクセスtoken>
client_id: <クライアントid>
client_secret: <クライアントsecret>
refresh_token: <リフレッシュtoken>

ログとGoogle Driveの画面です。 閲覧者がダウンロード可というのはオーナーの私限定です、念のため。

2019-07-03 14:22:53 起動
2019-07-03 14:22:53 設定を読み込み
2019-07-03 14:22:53 トークンを生成
2019-07-03 14:23:37 トークンを保存
2019-07-03 14:23:37 アーカイブ処理
2019-07-03 14:23:37 コマンドライン:tar --create --gzip --file=hp_backup_2019-07-03.gz --directory=../myapp .
2019-07-03 14:23:43 バックアップファイル:104739555バイト
2019-07-03 14:23:43 アップロード開始
2019-07-03 14:26:04 アップロード結果
2019-07-03 14:26:04 $VAR1 = {
          'name' => 'hp_backup_2019-07-03.gz',
	  'id' => '<Google Driveでのファイルid>',
          'kind' => 'drive#file',
          'mimeType' => 'application/x-gzip'
        };

2019-07-03 14:26:04 削除
2019-07-03 14:26:04 終了