From 25bb1c00db047a451e932071fb796de967f60113 Mon Sep 17 00:00:00 2001 From: gitolite tester Date: Tue, 27 Mar 2012 22:06:43 +0530 Subject: [PATCH] mirroring without sausages (or at least without showing the making of said sausages) --- src/Gitolite/Triggers/Mirroring.pm | 212 ++++++++++++++ src/commands/mirror | 69 +++++ t/mirror-test | 435 +++++++++++++++++++++++++++++ t/mirror-test-rc | 127 +++++++++ t/mirror-test-setup.sh | 193 +++++++++++++ t/mirror-test-ssh-config | 11 + 6 files changed, 1047 insertions(+) create mode 100644 src/Gitolite/Triggers/Mirroring.pm create mode 100755 src/commands/mirror create mode 100755 t/mirror-test create mode 100644 t/mirror-test-rc create mode 100755 t/mirror-test-setup.sh create mode 100644 t/mirror-test-ssh-config diff --git a/src/Gitolite/Triggers/Mirroring.pm b/src/Gitolite/Triggers/Mirroring.pm new file mode 100644 index 0000000..430f662 --- /dev/null +++ b/src/Gitolite/Triggers/Mirroring.pm @@ -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; diff --git a/src/commands/mirror b/src/commands/mirror new file mode 100755 index 0000000..004ee38 --- /dev/null +++ b/src/commands/mirror @@ -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 +Usage 2: ssh git@master-server mirror push + +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}; +} diff --git a/t/mirror-test b/t/mirror-test new file mode 100755 index 0000000..8793083 --- /dev/null +++ b/t/mirror-test @@ -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'/ +"; diff --git a/t/mirror-test-rc b/t/mirror-test-rc new file mode 100644 index 0000000..c5752bb --- /dev/null +++ b/t/mirror-test-rc @@ -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: diff --git a/t/mirror-test-setup.sh b/t/mirror-test-setup.sh new file mode 100755 index 0000000..23f382d --- /dev/null +++ b/t/mirror-test-setup.sh @@ -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 ====================================================================== diff --git a/t/mirror-test-ssh-config b/t/mirror-test-ssh-config new file mode 100644 index 0000000..40de6d7 --- /dev/null +++ b/t/mirror-test-ssh-config @@ -0,0 +1,11 @@ +host frodo + user frodo + hostname localhost + +host sam + user sam + hostname localhost + +host gollum + user gollum + hostname localhost