gitolite/contrib/adc/s3backup

151 lines
3.8 KiB
Perl

#!/usr/bin/perl
# Copyright 2011, David Bremner <bremner@debian.org>
#
# This add-on to gitolite is licensed under the same terms as gitolite
# At the time of this writing this is GPL-2, but I also grant
# Sitaram Chamarty the right to re-license as he chooses.
=pod
S3BACKUP - Backup the whole gitolite home directory to amazon s3.
RUNNING
To run it (assuming you call this "s3backup")
As an ADC, only by a user with read access to gitolite-admin :
ssh git@server s3backup [-v error|warning|notice|info|debug] [subcommands]
You must pass the AWS_SECRET_ACCESS_KEY on stdin.
SUBCOMMANDS
You may optionally pass one of the following sub commands
=over
=item incr|full
Run incremental or full backup. By default, leave it to duplicity to
guess.
=item prune
remove all but $S3_KEEP_FULL (see Configuration below)
=back
OPTIONS
-v specifies a log level, passed straight to duplicity.
incr or full specify backup level
CONFIGURATION
Make a file ~git/.s3backup.rc that looks like
$S3_EUROPE=0; # 1 to store in europe
# (only matters at creation)
$S3_KEEP_FULL=3; # keep 3 full backups
$S3_BUCKET="my_bucket"; # s3 bucket name, will be created
# if it doesn't exist.
$S3_AWS_KEY_ID="ABADABA57DOO"; # this is the _non_ secret one
$S3_ENCRYPT_KEY="DEADBEEF AA232332"; # gpg keys, space delimited.
# note that duplicity will abort if
# these keys are not trusted
=cut
use strict;
use warnings;
die "ENV GL_RC not set\n" unless $ENV{GL_RC};
die "ENV GL_BINDIR not set\n" unless $ENV{GL_BINDIR};
unshift @INC, $ENV{GL_BINDIR};
require gitolite or die "parse gitolite.pm failed\n";
gitolite->import;
my ($perm, $creator) = check_access("gitolite-admin");
die "no read access to gitolite-admin\n" unless $perm =~ /R/;
our ($S3_EUROPE, $S3_BUCKET, $S3_AWS_KEY_ID, $S3_ENCRYPT_KEYS, $S3_KEEP_FULL);
chdir($ENV{HOME}) or die "chdir $ENV{HOME} : $!\n";
my $configfile = $ENV{HOME} . "/.s3backup.rc";
do $configfile or die "error parsing $configfile\n";
# ANCHOR ALL PATTERNS
die 'bad value for $S3_EUROPE'
unless (defined($S3_EUROPE) && $S3_EUROPE =~ m/^0|1$/);
die 'bad value for $S3_KEEP_FULL'
unless (defined($S3_KEEP_FULL) && $S3_KEEP_FULL =~ m/^[0-9]+$/);
die 'bad value for $S3_BACKUP'
unless (defined($S3_BUCKET) && $S3_BUCKET =~ m/^[a-z\-_0-9\.]+$/);
die 'bad value for $S3_AWS_KEY_ID'
unless (defined($S3_AWS_KEY_ID) && $S3_AWS_KEY_ID =~ m/^[A-Z0-9]+$/);
die 'bad value for $S3_ENCRYPT_KEYS'
unless (defined($S3_ENCRYPT_KEYS) &&
$S3_ENCRYPT_KEYS =~ m/^[a-fA-F0-9]+(\s+[a-fA-F0-9]+)*/);
my $verbosity='notice';
if (scalar(@ARGV)>0 and $ARGV[0] eq '-v'){
shift;
$verbosity = shift;
}
die "bad verbosity" unless ($verbosity =~ m/^(error|warning|notice|info|debug)$/);
my $subcommand=shift || 'default';
die "bad subcommand" if (defined($subcommand) &&
$subcommand !~ m/^(incr|full|prune|default)$/);
$ENV{AWS_ACCESS_KEY_ID}=$S3_AWS_KEY_ID;
chomp($ENV{AWS_SECRET_ACCESS_KEY}=<>) or
die "must pass SECRET_ACCESS_KEY on stdin";
my @args=();
if ($subcommand ne 'default' ){
if ($subcommand eq 'prune') {
push(@args, 'remove-all-but-n-full', $S3_KEEP_FULL, '--force');
} else {
push(@args, $subcommand);
}
}
push(@args, '--verb', $verbosity);
push(@args, '--s3-use-new-style');
foreach my $key (split(' ',$S3_ENCRYPT_KEYS)){
push(@args, '--encrypt-key', $key);
}
push(@args, '--s3-european-buckets') if ($S3_EUROPE);
push(@args, $ENV{HOME}) unless ($subcommand eq 'prune');
push(@args, 's3+http://'.$S3_BUCKET);
my $semaphore=$ENV{HOME}."/.gitolite.down";
die "$semaphore already exists" if (-f $semaphore);
eval {
open (SEMFD,'>',$semaphore) or die ("failed to open $semaphore");
my $now = gmtime();
print SEMFD "Repo unavailable due to $subcommand backup started at $now GMT\n";
close SEMFD;
system '/usr/bin/duplicity', @args;
};
unlink $semaphore;