mirroring without sausages
(or at least without showing the making of said sausages)
This commit is contained in:
parent
b78466b164
commit
25bb1c00db
6 changed files with 1047 additions and 0 deletions
212
src/Gitolite/Triggers/Mirroring.pm
Normal file
212
src/Gitolite/Triggers/Mirroring.pm
Normal file
|
@ -0,0 +1,212 @@
|
|||
package Gitolite::Triggers::Mirroring;
|
||||
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
|
||||
my $hn = $rc{HOSTNAME};
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub input {
|
||||
return unless $ARGV[0] =~ /^server-(\S+)$/;
|
||||
|
||||
# note: we treat %rc as our own internal "poor man's %ENV"
|
||||
$rc{FROM_SERVER} = $1;
|
||||
trace(3, "from_server: $1");
|
||||
|
||||
if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/ ) {
|
||||
# my ($user, $newsoc, $repo) = ($1, $2, $3);
|
||||
$ENV{SSH_ORIGINAL_COMMAND} = $2;
|
||||
@ARGV = ($1);
|
||||
$rc{REDIRECTED_PUSH} = 1;
|
||||
trace(3, "redirected_push for user $1");
|
||||
} else {
|
||||
# master -> slave push, no access checks needed
|
||||
$ENV{GL_BYPASS_ACCESS_CHECKS} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
my ($mode, $master, %slaves, %trusted_slaves);
|
||||
|
||||
sub pre_git {
|
||||
return unless $hn;
|
||||
# nothing, and I mean NOTHING, happens if HOSTNAME is not set
|
||||
trace(1, "pre_git() on $hn");
|
||||
|
||||
my ($repo, $user, $aa) = @_[1, 2, 3];
|
||||
|
||||
my $sender = $rc{FROM_SERVER} || '';
|
||||
$user = '' if $sender and not exists $rc{REDIRECTED_PUSH};
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# now you know the repo, get its mirroring details
|
||||
details($repo);
|
||||
|
||||
# we don't deal with any reads. Note that for pre-git this check must
|
||||
# happen *after* getting details, to give mode() a chance to die on "known
|
||||
# unknown" repos (repos that are in the config, but mirror settings
|
||||
# exclude this host from both the master and slave lists)
|
||||
return if $aa eq 'R';
|
||||
|
||||
trace(1, "mirror", "pre_git", $repo, "user=$user", "sender=$sender", "mode=$mode",
|
||||
($rc{REDIRECTED_PUSH} ? ("redirected") : ()));
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 1: we're master or slave, normal user pushing to us
|
||||
if ($user and not $rc{REDIRECTED_PUSH}) {
|
||||
trace(3, "case 1, user push");
|
||||
return if $mode eq 'local' or $mode eq 'master';
|
||||
if ($trusted_slaves{$hn}) {
|
||||
trace(3, "redirecting to $master");
|
||||
trace(1, "redirect to $master");
|
||||
exec("ssh", $master, "USER=$user", "SOC=$ENV{SSH_ORIGINAL_COMMAND}");
|
||||
} else {
|
||||
_die "$hn: pushing '$repo' to slave '$hn' not allowed";
|
||||
}
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 2: we're slave, master pushing to us
|
||||
if ($sender and not $rc{REDIRECTED_PUSH}) {
|
||||
trace(3, "case 2, master push");
|
||||
_die "$hn: '$repo' is local" if $mode eq 'local';
|
||||
_die "$hn: '$repo' is native" if $mode eq 'master';
|
||||
_die "$hn: '$sender' is not the master for '$repo'" if $master ne $sender;
|
||||
return;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 3: we're master, slave sending a redirected push to us
|
||||
if ($sender and $rc{REDIRECTED_PUSH}) {
|
||||
trace(3, "case 2, slave redirect");
|
||||
_die "$hn: '$repo' is local" if $mode eq 'local';
|
||||
_die "$hn: '$repo' is not native" if $mode eq 'slave';
|
||||
_die "$hn: '$sender' is not a valid slave for '$repo'" if not $slaves{$sender};
|
||||
_die "$hn: redirection not allowed from '$sender'" if not $trusted_slaves{$sender};
|
||||
return;
|
||||
}
|
||||
|
||||
_die "$hn: should not reach this line";
|
||||
|
||||
}
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
sub post_git {
|
||||
return unless $hn;
|
||||
# nothing, and I mean NOTHING, happens if HOSTNAME is not set
|
||||
trace(1, "post_git() on $hn");
|
||||
|
||||
my ($repo, $user, $aa) = @_[1, 2, 3];
|
||||
# we don't deal with any reads
|
||||
return if $aa eq 'R';
|
||||
|
||||
my $sender = $rc{FROM_SERVER} || '';
|
||||
$user = '' if $sender;
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# now you know the repo, get its mirroring details
|
||||
details($repo);
|
||||
|
||||
trace(1, "mirror", "post_git", $repo, "user=$user", "sender=$sender", "mode=$mode",
|
||||
($rc{REDIRECTED_PUSH} ? ("redirected") : ()));
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 1: we're master or slave, normal user pushing to us
|
||||
if ($user and not $rc{REDIRECTED_PUSH}) {
|
||||
trace(3, "case 1, user push");
|
||||
return if $mode eq 'local';
|
||||
# slave was eliminated earlier anyway, so that leaves 'master'
|
||||
|
||||
# find all slaves and push to each of them
|
||||
push_to_slaves($repo);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 2: we're slave, master pushing to us
|
||||
if ($sender and not $rc{REDIRECTED_PUSH}) {
|
||||
trace(3, "case 2, master push");
|
||||
# nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# case 3: we're master, slave sending a redirected push to us
|
||||
if ($sender and $rc{REDIRECTED_PUSH}) {
|
||||
trace(3, "case 2, slave redirect");
|
||||
|
||||
# find all slaves and push to each of them
|
||||
push_to_slaves($repo);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
my $lastrepo = '';
|
||||
|
||||
sub details {
|
||||
my $repo = shift;
|
||||
return if $lastrepo eq $repo;
|
||||
|
||||
$master = master($repo);
|
||||
%slaves = slaves($repo);
|
||||
$mode = mode($repo);
|
||||
%trusted_slaves = trusted_slaves($repo);
|
||||
trace(3, $master, $mode, join(",", sort keys %slaves), join(",", sort keys %trusted_slaves) );
|
||||
}
|
||||
|
||||
sub master {
|
||||
return option(+shift, 'mirror.master');
|
||||
}
|
||||
|
||||
sub slaves {
|
||||
my $ref = git_config(+shift, "^gitolite-options\\.mirror\\.slaves.*");
|
||||
my %out = map { $_ => 1 } map { split } values %$ref;
|
||||
return %out;
|
||||
}
|
||||
|
||||
sub trusted_slaves {
|
||||
my $ref = git_config(+shift, "^gitolite-options\\.mirror\\.redirectOK.*");
|
||||
# the list of trusted slaves (where we accept redirected pushes from)
|
||||
# is either explicitly given...
|
||||
my @out = map { split } values %$ref;
|
||||
my %out = map { $_ => 1 } @out;
|
||||
# ...or it's all the slaves mentioned if the list is just a "all"
|
||||
%out = %slaves if (@out == 1 and $out[0] eq 'all');
|
||||
return %out;
|
||||
}
|
||||
|
||||
sub mode {
|
||||
my $repo = shift;
|
||||
return 'local' if not $hn;
|
||||
return 'master' if $master eq $hn;
|
||||
return 'slave' if $slaves{$hn};
|
||||
return 'local' if not $master and not %slaves;
|
||||
_die "$hn: '$repo' is mirrored but not here";
|
||||
}
|
||||
}
|
||||
|
||||
sub push_to_slaves {
|
||||
my $repo = shift;
|
||||
|
||||
my $u = $ENV{GL_USER};
|
||||
delete $ENV{GL_USER}; # why? see src/commands/mirror
|
||||
|
||||
for my $s (sort keys %slaves) {
|
||||
system("gitolite mirror push $s $repo &");
|
||||
}
|
||||
|
||||
$ENV{GL_USER} = $u;
|
||||
}
|
||||
|
||||
1;
|
69
src/commands/mirror
Executable file
69
src/commands/mirror
Executable file
|
@ -0,0 +1,69 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
my $tid;
|
||||
BEGIN {
|
||||
$tid = $ENV{GL_TID} || 0;
|
||||
delete $ENV{GL_TID};
|
||||
}
|
||||
|
||||
use lib $ENV{GL_BINDIR};
|
||||
use Gitolite::Rc;
|
||||
use Gitolite::Common;
|
||||
use Gitolite::Conf::Load;
|
||||
|
||||
=for usage
|
||||
Usage 1: gitolite mirror push <slave> <repo>
|
||||
Usage 2: ssh git@master-server mirror push <slave> <repo>
|
||||
|
||||
Forces a push of one repo to one slave.
|
||||
|
||||
Usage 1 is directly on the master server. Nothing is checked; if the slave
|
||||
accepts it, the push happens, even if the slave is not in any slaves
|
||||
option. This is how you do delayed or lagged pushes to servers that do not
|
||||
need real-time updates or have bandwidth/connectivity issues.
|
||||
|
||||
Usage 2 can be initiated by *any* user who has *any* gitolite access to the
|
||||
master server, but it checks that the slave is in one of the slaves options
|
||||
before doing the push.
|
||||
=cut
|
||||
|
||||
usage() if not @ARGV or $ARGV[0] eq '-h';
|
||||
|
||||
_die "HOSTNAME not set" if not $rc{HOSTNAME};
|
||||
|
||||
my ($cmd, $host, $repo) = @ARGV;
|
||||
usage() if not $repo;
|
||||
|
||||
if ($cmd eq 'push') {
|
||||
valid_slave($host, $repo) if exists $ENV{GL_USER};
|
||||
# will die if host not in slaves for repo
|
||||
|
||||
trace(1, "TID=$tid host=$host repo=$repo", "gitolite mirror push started");
|
||||
_chdir($rc{GL_REPO_BASE});
|
||||
_chdir("$repo.git");
|
||||
|
||||
my $errors = 0;
|
||||
for (`git push --mirror $host:$repo 2>&1`) {
|
||||
print STDERR "$_" if -t STDERR or exists $ENV{GL_USER};
|
||||
chomp;
|
||||
if (/FATAL/) {
|
||||
$errors++;
|
||||
gl_log('mirror', $_);
|
||||
} else {
|
||||
trace(1, "mirror: $_");
|
||||
}
|
||||
}
|
||||
exit $errors;
|
||||
}
|
||||
|
||||
sub valid_slave {
|
||||
my ($host, $repo) = @_;
|
||||
_die "invalid repo '$repo'" unless $repo =~ $REPONAME_PATT;
|
||||
|
||||
my $ref = git_config($repo, "^gitolite-options\\.mirror\\.slaves.*");
|
||||
my %list = map { $_ => 1 } map { split } values %$ref;
|
||||
|
||||
_die "'$host' not a valid slave for '$repo'" unless $list{$host};
|
||||
}
|
435
t/mirror-test
Executable file
435
t/mirror-test
Executable file
|
@ -0,0 +1,435 @@
|
|||
#!/usr/bin/perl
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
# you need 3 disposable userids: sam, frodo, gollum. Then the test user (say
|
||||
# "g3") needs to be able to sudo into them. Put this in /etc/sudoers:
|
||||
|
||||
# g3 ALL = (sam,frodo,gollum) NOPASSWD: ALL
|
||||
|
||||
$ENV{TSH_ERREXIT} = 1;
|
||||
|
||||
# this is hardcoded; change it if needed
|
||||
use lib "src";
|
||||
use Gitolite::Test;
|
||||
use Cwd;
|
||||
my $workdir = getcwd();
|
||||
my $h = $ENV{HOME};
|
||||
my ($t, $t2); # temp vars
|
||||
|
||||
# basic tests
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
try "plan 152";
|
||||
## try "DEF POK = !/DENIED/; !/failed to push/";
|
||||
|
||||
## confreset;confadd '
|
||||
|
||||
## ';
|
||||
|
||||
## try "ADMIN_PUSH set1; !/FATAL/" or die text();
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
# switch keys
|
||||
sub swk {
|
||||
my $h = $ENV{HOME};
|
||||
my $k = shift;
|
||||
system("cp $h/.ssh/$k $h/.ssh/id_rsa");
|
||||
system("cp $h/.ssh/$k.pub $h/.ssh/id_rsa.pub");
|
||||
}
|
||||
|
||||
sub all {
|
||||
try "F " . join(" ", @_);
|
||||
try "S " . join(" ", @_);
|
||||
try "G " . join(" ", @_);
|
||||
}
|
||||
|
||||
try "
|
||||
DEF F = sudo -u frodo -i
|
||||
DEF S = sudo -u sam -i
|
||||
DEF G = sudo -u gollum -i
|
||||
";
|
||||
|
||||
my $bd = `gitolite query-rc -n GL_BINDIR`;
|
||||
|
||||
try "
|
||||
$bd/../t/mirror-test-setup.sh; ok or die mirror setup shell script failed
|
||||
/hello server-frodo, this is frodo/
|
||||
/hello server-sam, this is frodo/
|
||||
/hello server-gollum, this is frodo/
|
||||
/hello server-frodo, this is sam/
|
||||
/hello server-sam, this is sam/
|
||||
/hello server-gollum, this is sam/
|
||||
/hello server-frodo, this is gollum/
|
||||
/hello server-sam, this is gollum/
|
||||
/hello server-gollum, this is gollum/
|
||||
/hello admin, this is frodo/
|
||||
/Initialized empty .*/gitolite-admin.git/
|
||||
/Initialized empty .*/r1.git/
|
||||
/Initialized empty .*/r2.git/
|
||||
/Initialized empty .*/testing.git/
|
||||
/Initialized empty .*/gitolite-admin.git/
|
||||
/Initialized empty .*/r1.git/
|
||||
/Initialized empty .*/r2.git/
|
||||
/Initialized empty .*/testing.git/
|
||||
/Initialized empty .*/gitolite-admin.git/
|
||||
/Initialized empty .*/r1.git/
|
||||
/Initialized empty .*/r2.git/
|
||||
/Initialized empty .*/testing.git/
|
||||
";
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# SECTION 1: gitolite-admin shenanigans
|
||||
|
||||
# push to frodo and see sam and gollum change
|
||||
try "
|
||||
git clone frodo\@localhost:gitolite-admin fga
|
||||
ok; /Cloning into 'fga'.../
|
||||
cd fga; ok
|
||||
cp $h/.ssh/u?.pub keydir; ok
|
||||
git add keydir; ok
|
||||
git commit -m 6keys; ok
|
||||
git push; ok
|
||||
/To frodo\@localhost:gitolite-admin/
|
||||
/master -> master/
|
||||
git rev-parse HEAD
|
||||
";
|
||||
|
||||
chomp($t = text());
|
||||
|
||||
try "
|
||||
git ls-remote sam\@localhost:gitolite-admin
|
||||
ok; /$t/
|
||||
git ls-remote gollum\@localhost:gitolite-admin
|
||||
ok; /$t/
|
||||
";
|
||||
|
||||
try "
|
||||
cd ..
|
||||
|
||||
";
|
||||
|
||||
# push to sam and see frodo and gollum change
|
||||
try "
|
||||
git clone sam\@localhost:gitolite-admin sga
|
||||
ok; /Cloning into 'sga'.../
|
||||
cd sga; ok
|
||||
empty; ok
|
||||
git push; ok
|
||||
/To sam\@localhost:gitolite-admin/
|
||||
/master -> master/
|
||||
git rev-parse HEAD
|
||||
";
|
||||
|
||||
chomp($t = text());
|
||||
|
||||
try "
|
||||
git ls-remote frodo\@localhost:gitolite-admin
|
||||
ok; /$t/
|
||||
git ls-remote gollum\@localhost:gitolite-admin
|
||||
ok; /$t/
|
||||
";
|
||||
|
||||
try "
|
||||
cd ..
|
||||
|
||||
";
|
||||
|
||||
# push to gollum and fail at gollum
|
||||
try "
|
||||
git clone gollum\@localhost:gitolite-admin gga
|
||||
ok; /Cloning into 'gga'.../
|
||||
cd gga; ok
|
||||
empty; ok
|
||||
git push; !ok
|
||||
!/To gollum\@localhost:gitolite-admin/
|
||||
!/master -> master/
|
||||
/gollum: pushing 'gitolite-admin' to slave 'gollum' not allowed/
|
||||
git rev-parse HEAD
|
||||
";
|
||||
|
||||
chomp($t2 = text());
|
||||
|
||||
try "
|
||||
git ls-remote frodo\@localhost:gitolite-admin
|
||||
ok; /$t/; !/$t2/
|
||||
git ls-remote sam\@localhost:gitolite-admin
|
||||
ok; /$t/; !/$t2/
|
||||
git ls-remote gollum\@localhost:gitolite-admin
|
||||
ok; /$t/; !/$t2/
|
||||
";
|
||||
|
||||
# fake out the gollum failure to continue the redirected push and fail at frodo
|
||||
try "
|
||||
sudo -u gollum -i gitolite git-config -r gitolite-admin .
|
||||
ok
|
||||
/redirectOK.*sam/
|
||||
!/redirectOK.*gollum/
|
||||
|
||||
sudo -u gollum -i bash -c 'echo repo gitolite-admin > junk'
|
||||
sudo -u gollum -i bash -c 'echo option mirror.redirectOK-1 = gollum >> junk'
|
||||
sudo -u gollum -i bash -c 'cat junk >> .gitolite/conf/gitolite.conf'
|
||||
sudo -u gollum -i gitolite compile
|
||||
sudo -u gollum -i gitolite git-config -r gitolite-admin .
|
||||
ok
|
||||
/redirectOK.*sam/
|
||||
/redirectOK.*gollum/
|
||||
|
||||
git push; !ok
|
||||
/frodo: redirection not allowed from 'gollum'/
|
||||
!/To gollum\@localhost:gitolite-admin/
|
||||
!/master -> master/
|
||||
";
|
||||
|
||||
# reset gollum via frodo
|
||||
try "
|
||||
cd ..
|
||||
rm -rf fga
|
||||
git clone frodo\@localhost:gitolite-admin fga
|
||||
ok; /Cloning into 'fga'.../
|
||||
cd fga; ok
|
||||
empty; ok
|
||||
git push; ok
|
||||
/To frodo\@localhost:gitolite-admin/
|
||||
/master -> master/
|
||||
|
||||
sudo -u gollum -i gitolite git-config -r gitolite-admin .
|
||||
ok
|
||||
/redirectOK.*sam/
|
||||
!/redirectOK.*gollum/
|
||||
|
||||
git rev-parse HEAD
|
||||
";
|
||||
|
||||
chomp($t = text());
|
||||
|
||||
try "
|
||||
git ls-remote sam\@localhost:gitolite-admin
|
||||
ok; /$t/
|
||||
git ls-remote gollum\@localhost:gitolite-admin
|
||||
ok; /$t/
|
||||
";
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# user repo shenanigans
|
||||
|
||||
# for a recap of the perms see t/mirror-test-setup.sh
|
||||
|
||||
try "
|
||||
cd ..
|
||||
pwd
|
||||
/tmp/tsh_tempdir/ or die not in the right place
|
||||
" or die;
|
||||
|
||||
swk('u1');
|
||||
|
||||
# u1 sam r1, R ok, W ok
|
||||
try "
|
||||
rm -rf fga sga gga
|
||||
|
||||
git clone sam\@localhost:r1 sr1
|
||||
/Cloning into 'sr1'.../
|
||||
/warning: You appear to have cloned an empty repository/
|
||||
cd sr1
|
||||
empty
|
||||
git push origin master
|
||||
/new branch/; /master -> master/
|
||||
git rev-parse HEAD
|
||||
";
|
||||
chomp($t = text());
|
||||
|
||||
# u1 sam r1, W mirrors to frodo but not gollum
|
||||
try "
|
||||
git ls-remote sam\@localhost:r1
|
||||
/$t/
|
||||
git ls-remote frodo\@localhost:r1
|
||||
/$t/
|
||||
git ls-remote gollum\@localhost:r1
|
||||
/gollum: 'r1' is mirrored but not here/
|
||||
";
|
||||
|
||||
swk("u2");
|
||||
try "
|
||||
empty
|
||||
git rev-parse HEAD
|
||||
";
|
||||
chomp($t2 = text());
|
||||
|
||||
# u2 sam r2 W ok, mirrors to all
|
||||
try "
|
||||
git push sam\@localhost:r2 master
|
||||
/new branch/; /master -> master/
|
||||
/master -> master/
|
||||
git ls-remote frodo\@localhost:r2
|
||||
!/$t/
|
||||
/$t2/
|
||||
git ls-remote gollum\@localhost:r2
|
||||
!/$t/
|
||||
/$t2/
|
||||
";
|
||||
|
||||
swk("u1");
|
||||
|
||||
# u1 gollum r1 -- "known unknown" :-)
|
||||
# u1 frodo r1 R ok, W not ok
|
||||
# u1 sam r1 R ok, W ok
|
||||
try "
|
||||
cd ..
|
||||
rm -rf sr1
|
||||
|
||||
git clone gollum\@localhost:r1 fr1
|
||||
/gollum: 'r1' is mirrored but not here/
|
||||
|
||||
git clone frodo\@localhost:r1 fr1; ok
|
||||
cd fr1
|
||||
empty
|
||||
git push
|
||||
/frodo: pushing 'r1' to slave 'frodo' not allowed/
|
||||
cd ..
|
||||
git clone sam\@localhost:r1 sr1; ok
|
||||
cd sr1
|
||||
empty
|
||||
git push; ok
|
||||
/master -> master/
|
||||
git rev-parse HEAD
|
||||
";
|
||||
chomp($t = text());
|
||||
|
||||
# u1 sam r1 W mirrored to frodo but not gollum
|
||||
try "
|
||||
git ls-remote sam\@localhost:r1
|
||||
/$t/
|
||||
git ls-remote frodo\@localhost:r1
|
||||
/$t/
|
||||
|
||||
git ls-remote gollum\@localhost:r1
|
||||
/gollum: 'r1' is mirrored but not here/
|
||||
|
||||
git reset --hard HEAD^; ok
|
||||
tc a
|
||||
git push; !ok
|
||||
/rejected/
|
||||
/failed to push/
|
||||
|
||||
git push -f
|
||||
/\\+ .......\\.\\.\\........ master -> master .forced update/
|
||||
";
|
||||
|
||||
swk("u2");
|
||||
|
||||
# u2 frodo r1 R ok, W not allowed (no redirectOK)
|
||||
# u2 frodo r2 W ok
|
||||
try "
|
||||
cd ..
|
||||
rm -rf fr1 sr1
|
||||
|
||||
git clone frodo\@localhost:r1 fr1; ok
|
||||
cd fr1
|
||||
tc b
|
||||
git push
|
||||
/frodo: pushing 'r1' to slave 'frodo' not allowed/
|
||||
cd ..
|
||||
git clone frodo\@localhost:r2 fr2; ok
|
||||
cd fr2
|
||||
tc c
|
||||
git push
|
||||
/master -> master/
|
||||
git rev-parse HEAD
|
||||
";
|
||||
chomp($t = text());
|
||||
|
||||
# u2 frodo r2 W mirrors to sam and gollum
|
||||
try "
|
||||
git ls-remote sam\@localhost:r2
|
||||
/$t/
|
||||
git ls-remote gollum\@localhost:r2
|
||||
/$t/
|
||||
|
||||
git reset --hard HEAD^; ok
|
||||
tc d
|
||||
git push
|
||||
/rejected/
|
||||
/failed to push/
|
||||
|
||||
git push -f
|
||||
/\\+ .......\\.\\.\\........ master -> master .forced update/
|
||||
|
||||
cd ..
|
||||
rm -rf fr1 fr2
|
||||
";
|
||||
|
||||
swk("u3");
|
||||
|
||||
# u3 frodo r2 R ok W ok
|
||||
try "
|
||||
git clone frodo\@localhost:r2 fr2; ok
|
||||
cd fr2
|
||||
tc e
|
||||
git push; ok
|
||||
|
||||
git rev-parse HEAD
|
||||
";
|
||||
chomp($t = text());
|
||||
|
||||
# u3 frodo r2 W mirrors to sam and gollum
|
||||
try "
|
||||
git ls-remote sam\@localhost:r2
|
||||
/$t/
|
||||
git ls-remote gollum\@localhost:r2
|
||||
/$t/
|
||||
|
||||
git reset --hard HEAD^; ok
|
||||
tc f
|
||||
git push
|
||||
/rejected/
|
||||
/failed to push/
|
||||
|
||||
sleep 10
|
||||
git push -f
|
||||
/\\+ refs/heads/master r2 u3 DENIED by fallthru/
|
||||
/hook declined/
|
||||
/rejected/
|
||||
";
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# all those vague edge cases where the two servers have totally wrong ideas
|
||||
# about each other
|
||||
|
||||
swk('u1');
|
||||
|
||||
try "sudo -u frodo -i ls .gitolite/logs";
|
||||
chomp($t = text());
|
||||
my $lfn = ".gitolite/logs/$t";
|
||||
|
||||
try "
|
||||
ssh sam\@localhost mirror push frodo lfrodo; !ok
|
||||
/FATAL: frodo: 'lfrodo' is local/
|
||||
|
||||
ssh sam\@localhost mirror push frodo mboth; !ok
|
||||
/FATAL: frodo: 'mboth' is native/
|
||||
|
||||
ssh sam\@localhost mirror push frodo mnotsam; !ok
|
||||
/FATAL: frodo: 'sam' is not the master for 'mnotsam'/
|
||||
|
||||
cd ..
|
||||
git clone sam\@localhost:lfrodo2 lfrodo2; ok
|
||||
cd lfrodo2
|
||||
empty
|
||||
git push origin master; !ok
|
||||
/FATAL: frodo: 'lfrodo2' is local/
|
||||
|
||||
cd ..
|
||||
git clone sam\@localhost:nnfrodo nnfrodo; ok
|
||||
cd nnfrodo
|
||||
empty
|
||||
git push origin master; !ok
|
||||
/FATAL: frodo: 'nnfrodo' is not native/
|
||||
|
||||
cd ..
|
||||
git clone sam\@localhost:nvsfrodo nvsfrodo; ok
|
||||
cd nvsfrodo
|
||||
empty
|
||||
git push origin master; !ok
|
||||
/FATAL: frodo: 'sam' is not a valid slave for 'nvsfrodo'/
|
||||
";
|
127
t/mirror-test-rc
Normal file
127
t/mirror-test-rc
Normal file
|
@ -0,0 +1,127 @@
|
|||
# configuration variables for gitolite
|
||||
|
||||
# This file is in perl syntax. But you do NOT need to know perl to edit it --
|
||||
# just mind the commas, use single quotes unless you know what you're doing,
|
||||
# and make sure the brackets and braces stay matched up!
|
||||
|
||||
# (Tip: perl allows a comma after the last item in a list also!)
|
||||
|
||||
%RC = (
|
||||
HOSTNAME => '%HOSTNAME',
|
||||
UMASK => 0077,
|
||||
GIT_CONFIG_KEYS => '',
|
||||
|
||||
# comment out if you don't need all the extra detail in the logfile
|
||||
LOG_EXTRA => 1,
|
||||
|
||||
# settings used by external programs; uncomment and change as needed. You
|
||||
# can add your own variables for use in your own external programs; take a
|
||||
# look at the cpu-time and desc commands for perl and shell samples.
|
||||
|
||||
# used by the cpu-time command
|
||||
# DISPLAY_CPU_TIME => 1,
|
||||
# CPU_TIME_WARN_LIMIT => 0.1,
|
||||
# used by the desc command
|
||||
# WRITER_CAN_UPDATE_DESC => 1,
|
||||
# used by the info command
|
||||
# SITE_INFO => 'Please see http://blahblah/gitolite for more help',
|
||||
|
||||
# add more roles (like MANAGER, TESTER, ...) here.
|
||||
# WARNING: if you make changes to this hash, you MUST run 'gitolite
|
||||
# compile' afterward, and possibly also 'gitolite trigger POST_COMPILE'
|
||||
ROLES =>
|
||||
{
|
||||
READERS => 1,
|
||||
WRITERS => 1,
|
||||
},
|
||||
# uncomment (and change) this if you wish
|
||||
# DEFAULT_ROLE_PERMS => 'READERS @all',
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these are available to remote users
|
||||
COMMANDS =>
|
||||
{
|
||||
'help' => 1,
|
||||
'info' => 1,
|
||||
'desc' => 1,
|
||||
'perms' => 1,
|
||||
'mirror' => 1,
|
||||
'writable' => 1,
|
||||
},
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence during the conf file parse
|
||||
SYNTACTIC_SUGAR =>
|
||||
[
|
||||
# 'continuation-lines',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence to modify the input (arguments and environment)
|
||||
INPUT =>
|
||||
[
|
||||
'Mirroring::input',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence just after the first access check is done
|
||||
ACCESS_1 =>
|
||||
[
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence at the start, before a git operation has started
|
||||
PRE_GIT =>
|
||||
[
|
||||
# if you use this, make this the first item in the list
|
||||
# 'renice 10',
|
||||
|
||||
'Mirroring::pre_git',
|
||||
|
||||
# see docs ("list of non-core programs shipped") for details
|
||||
# 'partial-copy',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence just after the second access check is done
|
||||
ACCESS_2 =>
|
||||
[
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence at the end, after a git operation has ended
|
||||
POST_GIT =>
|
||||
[
|
||||
# if you use this, make this the last item in the list
|
||||
# 'cpu-time',
|
||||
'Mirroring::post_git',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence after a new wild repo is created
|
||||
POST_CREATE =>
|
||||
[
|
||||
'post-compile/update-git-configs',
|
||||
'post-compile/update-gitweb-access-list',
|
||||
'post-compile/update-git-daemon-access-list',
|
||||
],
|
||||
|
||||
# comment out or uncomment as needed
|
||||
# these will run in sequence after post-update
|
||||
POST_COMPILE =>
|
||||
[
|
||||
'post-compile/ssh-authkeys',
|
||||
'post-compile/update-git-configs',
|
||||
'post-compile/update-gitweb-access-list',
|
||||
'post-compile/update-git-daemon-access-list',
|
||||
],
|
||||
);
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# per perl rules, this should be the last line in such a file:
|
||||
1;
|
||||
|
||||
# Local variables:
|
||||
# mode: perl
|
||||
# End:
|
||||
# vim: set syn=perl:
|
193
t/mirror-test-setup.sh
Executable file
193
t/mirror-test-setup.sh
Executable file
|
@ -0,0 +1,193 @@
|
|||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
hosts="frodo sam gollum"
|
||||
mainhost=frodo
|
||||
|
||||
# setup software
|
||||
bd=`gitolite query-rc -n GL_BINDIR`
|
||||
rm -rf /tmp/g3/src
|
||||
cp -a $bd /tmp/g3/src
|
||||
chmod -R go+rX /tmp/g3
|
||||
|
||||
# setup symlinks in frodo, sam, and gollum's accounts
|
||||
for h in $hosts
|
||||
do
|
||||
sudo -u $h -i bash -c "rm -rf *.pub bin .ssh projects.list repositories .gitolite .gitolite.rc"
|
||||
done
|
||||
|
||||
[ "$1" = "clear" ] && exit
|
||||
|
||||
cd /tmp/g3
|
||||
[ -d keys ] || {
|
||||
mkdir keys
|
||||
cd keys
|
||||
for h in $hosts
|
||||
do
|
||||
ssh-keygen -N '' -q -f server-$h -C $h
|
||||
chmod go+r /tmp/g3/keys/server-$h
|
||||
done
|
||||
cp $bd/../t/mirror-test-ssh-config ssh-config
|
||||
}
|
||||
|
||||
for h in $hosts
|
||||
do
|
||||
sudo -u $h -i bash -c "mkdir -p bin; ln -sf /tmp/g3/src/gitolite bin; mkdir -p .ssh; chmod 0700 .ssh"
|
||||
|
||||
sudo -u $h -i cp /tmp/g3/keys/ssh-config .ssh/config
|
||||
sudo -u $h -i cp /tmp/g3/keys/server-$h .ssh/id_rsa
|
||||
sudo -u $h -i cp /tmp/g3/keys/server-$h.pub .ssh/id_rsa.pub
|
||||
sudo -u $h -i chmod go-rwx .ssh/id_rsa .ssh/config
|
||||
|
||||
done
|
||||
|
||||
# add all pubkeys to all servers
|
||||
for h in $hosts
|
||||
do
|
||||
sudo -u $h -i gitolite setup -a admin
|
||||
for j in $hosts
|
||||
do
|
||||
sudo -u $h -i gitolite setup -pk /tmp/g3/keys/server-$j.pub
|
||||
echo sudo _u $j _i ssh $h@localhost info
|
||||
sudo -u $j -i ssh -o StrictHostKeyChecking=no $h@localhost info
|
||||
done
|
||||
echo ----
|
||||
done
|
||||
|
||||
# now copy our admin key to the main host
|
||||
cd;cd .ssh
|
||||
cp admin id_rsa; cp admin.pub id_rsa.pub
|
||||
cp admin.pub /tmp/g3/keys; chmod go+r /tmp/g3/keys/admin.pub
|
||||
sudo -u $mainhost -i gitolite setup -pk /tmp/g3/keys/admin.pub
|
||||
ssh $mainhost@localhost info
|
||||
|
||||
lines="
|
||||
repo gitolite-admin
|
||||
option mirror.master = frodo
|
||||
option mirror.slaves-1 = sam gollum
|
||||
option mirror.redirectOK = sam
|
||||
|
||||
repo r1
|
||||
RW+ = u1
|
||||
RW = u2
|
||||
R = u3
|
||||
option mirror.master = sam
|
||||
option mirror.slaves-1 = frodo
|
||||
|
||||
repo r2
|
||||
RW+ = u2
|
||||
RW = u3
|
||||
R = u4
|
||||
option mirror.master = sam
|
||||
option mirror.slaves-1 = frodo gollum
|
||||
option mirror.redirectOK = all
|
||||
|
||||
include \"%HOSTNAME.conf\"
|
||||
"
|
||||
|
||||
lines2="
|
||||
repo l-%HOSTNAME
|
||||
RW = u1
|
||||
"
|
||||
|
||||
# for each server, set the HOSTNAME to the rc, add the mirror options to the
|
||||
# conf file, and compile
|
||||
for h in $hosts
|
||||
do
|
||||
cat $bd/../t/mirror-test-rc | perl -pe "s/%HOSTNAME/$h/" > /tmp/g3/temp
|
||||
sudo -u $h -i cp /tmp/g3/temp .gitolite.rc
|
||||
echo "$lines" | sudo -u $h -i sh -c 'cat >> .gitolite/conf/gitolite.conf'
|
||||
echo "$lines2" | sudo -u $h -i sh -c "cat >> .gitolite/conf/$h.conf"
|
||||
sudo -u $h -i gitolite setup
|
||||
done
|
||||
|
||||
# goes on frodo
|
||||
lines="
|
||||
# local to frodo but sam thinks frodo is a slave
|
||||
repo lfrodo
|
||||
RW = u1
|
||||
|
||||
# both think they're master
|
||||
repo mboth
|
||||
RW = u1
|
||||
option mirror.master = frodo
|
||||
option mirror.slaves = sam
|
||||
|
||||
# frodo thinks someone else is the master but sam thinks he is
|
||||
repo mnotsam
|
||||
RW = u1
|
||||
option mirror.master = merry
|
||||
option mirror.slaves = frodo
|
||||
|
||||
# local to frodo but sam thinks frodo is a master and redirect is OK
|
||||
repo lfrodo2
|
||||
RW = u1
|
||||
|
||||
# non-native to frodo but sam thinks frodo is master
|
||||
repo nnfrodo
|
||||
RW = u1
|
||||
option mirror.master = gollum
|
||||
option mirror.slaves = frodo
|
||||
option mirror.redirectOK = all
|
||||
|
||||
# sam is not a valid slave to send stuff to frodo
|
||||
repo nvsfrodo
|
||||
RW = u1
|
||||
option mirror.master = frodo
|
||||
option mirror.slaves = gollum
|
||||
option mirror.redirectOK = all
|
||||
"
|
||||
|
||||
echo "$lines" | sudo -u frodo -i sh -c "cat >> .gitolite/conf/frodo.conf"
|
||||
|
||||
# goes on sam
|
||||
lines="
|
||||
# local to frodo but sam thinks frodo is a slave
|
||||
repo lfrodo
|
||||
RW = u1
|
||||
option mirror.master = sam
|
||||
option mirror.slaves = frodo
|
||||
|
||||
# both think they're master
|
||||
repo mboth
|
||||
RW = u1
|
||||
option mirror.master = sam
|
||||
option mirror.slaves = frodo
|
||||
|
||||
# frodo thinks someone else is the master but sam thinks he is
|
||||
repo mnotsam
|
||||
RW = u1
|
||||
option mirror.master = sam
|
||||
option mirror.slaves = frodo
|
||||
|
||||
# local to frodo but sam thinks frodo is a master and redirect is OK
|
||||
repo lfrodo2
|
||||
RW = u1
|
||||
option mirror.master = frodo
|
||||
option mirror.slaves = sam
|
||||
option mirror.redirectOK = all
|
||||
|
||||
# non-native to frodo but sam thinks frodo is master
|
||||
repo nnfrodo
|
||||
RW = u1
|
||||
option mirror.master = frodo
|
||||
option mirror.slaves = sam
|
||||
option mirror.redirectOK = all
|
||||
|
||||
# sam is not a valid slave to send stuff to frodo
|
||||
repo nvsfrodo
|
||||
RW = u1
|
||||
option mirror.master = frodo
|
||||
option mirror.slaves = sam
|
||||
option mirror.redirectOK = all
|
||||
"
|
||||
|
||||
echo "$lines" | sudo -u sam -i sh -c "cat >> .gitolite/conf/sam.conf"
|
||||
|
||||
for h in $hosts
|
||||
do
|
||||
sudo -u $h -i gitolite setup
|
||||
done
|
||||
|
||||
# that ends the setup phase
|
||||
echo ======================================================================
|
11
t/mirror-test-ssh-config
Normal file
11
t/mirror-test-ssh-config
Normal file
|
@ -0,0 +1,11 @@
|
|||
host frodo
|
||||
user frodo
|
||||
hostname localhost
|
||||
|
||||
host sam
|
||||
user sam
|
||||
hostname localhost
|
||||
|
||||
host gollum
|
||||
user gollum
|
||||
hostname localhost
|
Loading…
Reference in a new issue