~ubuntu-branches/ubuntu/utopic/gitolite3/utopic

« back to all changes in this revision

Viewing changes to src/lib/Gitolite/Triggers/Mirroring.pm

  • Committer: Package Import Robot
  • Author(s): David Bremner
  • Date: 2013-05-18 17:59:21 UTC
  • Revision ID: package-import@ubuntu.com-20130518175921-ac4xe6vd0jtxvjot
Tags: upstream-3.5.1+4
ImportĀ upstreamĀ versionĀ 3.5.1+4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
package Gitolite::Triggers::Mirroring;
 
2
 
 
3
use Gitolite::Rc;
 
4
use Gitolite::Common;
 
5
use Gitolite::Conf::Load;
 
6
 
 
7
use strict;
 
8
use warnings;
 
9
 
 
10
my $git_commands = "git-upload-pack|git-receive-pack|git-upload-archive";
 
11
my $hn           = $rc{HOSTNAME};
 
12
 
 
13
my ( $mode, $master, %slaves, %trusted_slaves );
 
14
 
 
15
# ----------------------------------------------------------------------
 
16
 
 
17
sub input {
 
18
    unless ($ARGV[0] =~ /^server-(\S+)$/) {
 
19
        _die "'$ARGV[0]' is not a valid server name" if $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/;
 
20
        return;
 
21
    }
 
22
 
 
23
    # note: we treat %rc as our own internal "poor man's %ENV"
 
24
    $rc{FROM_SERVER} = $1;
 
25
    trace( 3, "from_server: $1" );
 
26
    my $sender = $rc{FROM_SERVER} || '';
 
27
 
 
28
    # custom peer-to-peer commands.  At present the only one is 'perms -c',
 
29
    # sent from a mirror command
 
30
    if ($ENV{SSH_ORIGINAL_COMMAND} =~ /^CREATOR=(\S+) perms -c '(\S+)'$/) {
 
31
        $ENV{GL_USER} = $1;
 
32
 
 
33
        my $repo = $2;
 
34
        details($repo);
 
35
        _die "$hn: '$repo' is local"  if $mode eq 'local';
 
36
        _die "$hn: '$repo' is native" if $mode eq 'master';
 
37
        _die "$hn: '$sender' is not the master for '$repo'" if $master ne $sender;
 
38
 
 
39
        # this expects valid perms content on STDIN
 
40
        _system("gitolite perms -c $repo");
 
41
 
 
42
        # we're done.  Yes, really...
 
43
        exit 0;
 
44
    }
 
45
 
 
46
    if ( $ENV{SSH_ORIGINAL_COMMAND} =~ /^USER=(\S+) SOC=(git-receive-pack '(\S+)')$/ ) {
 
47
        # my ($user, $newsoc, $repo) = ($1, $2, $3);
 
48
        $ENV{SSH_ORIGINAL_COMMAND} = $2;
 
49
        @ARGV                      = ($1);
 
50
        $rc{REDIRECTED_PUSH}       = 1;
 
51
        trace( 3, "redirected_push for user $1" );
 
52
    } else {
 
53
        # master -> slave push, no access checks needed
 
54
        $ENV{GL_BYPASS_ACCESS_CHECKS} = 1;
 
55
    }
 
56
}
 
57
 
 
58
# ----------------------------------------------------------------------
 
59
 
 
60
sub pre_git {
 
61
    return unless $hn;
 
62
    # nothing, and I mean NOTHING, happens if HOSTNAME is not set
 
63
    trace( 1, "pre_git() on $hn" );
 
64
 
 
65
    my ( $repo, $user, $aa ) = @_[ 1, 2, 3 ];
 
66
 
 
67
    my $sender = $rc{FROM_SERVER} || '';
 
68
    $user = '' if $sender and not exists $rc{REDIRECTED_PUSH};
 
69
 
 
70
    # ------------------------------------------------------------------
 
71
    # now you know the repo, get its mirroring details
 
72
    details($repo);
 
73
 
 
74
    # we don't deal with any reads.  Note that for pre-git this check must
 
75
    # happen *after* getting details, to give mode() a chance to die on "known
 
76
    # unknown" repos (repos that are in the config, but mirror settings
 
77
    # exclude this host from both the master and slave lists)
 
78
    return if $aa eq 'R';
 
79
 
 
80
    trace( 1, "mirror", "pre_git", $repo, "user=$user", "sender=$sender", "mode=$mode", ( $rc{REDIRECTED_PUSH} ? ("redirected") : () ) );
 
81
 
 
82
    # ------------------------------------------------------------------
 
83
    # case 1: we're master or slave, normal user pushing to us
 
84
    if ( $user and not $rc{REDIRECTED_PUSH} ) {
 
85
        trace( 3, "case 1, user push" );
 
86
        return if $mode eq 'local' or $mode eq 'master';
 
87
        if ( $trusted_slaves{$hn} ) {
 
88
            trace( 3, "redirecting to $master" );
 
89
            trace( 1, "redirect to $master" );
 
90
            exec( "ssh", $master, "USER=$user", "SOC=$ENV{SSH_ORIGINAL_COMMAND}" );
 
91
        } else {
 
92
            _die "$hn: pushing '$repo' to slave '$hn' not allowed";
 
93
        }
 
94
    }
 
95
 
 
96
    # ------------------------------------------------------------------
 
97
    # case 2: we're slave, master pushing to us
 
98
    if ( $sender and not $rc{REDIRECTED_PUSH} ) {
 
99
        trace( 3, "case 2, master push" );
 
100
        _die "$hn: '$repo' is local"  if $mode eq 'local';
 
101
        _die "$hn: '$repo' is native" if $mode eq 'master';
 
102
        _die "$hn: '$sender' is not the master for '$repo'" if $master ne $sender;
 
103
        return;
 
104
    }
 
105
 
 
106
    # ------------------------------------------------------------------
 
107
    # case 3: we're master, slave sending a redirected push to us
 
108
    if ( $sender and $rc{REDIRECTED_PUSH} ) {
 
109
        trace( 3, "case 2, slave redirect" );
 
110
        _die "$hn: '$repo' is local"      if $mode eq 'local';
 
111
        _die "$hn: '$repo' is not native" if $mode eq 'slave';
 
112
        _die "$hn: '$sender' is not a valid slave for '$repo'" if not $slaves{$sender};
 
113
        _die "$hn: redirection not allowed from '$sender'"     if not $trusted_slaves{$sender};
 
114
        return;
 
115
    }
 
116
 
 
117
    _die "$hn: should not reach this line";
 
118
 
 
119
}
 
120
 
 
121
# ----------------------------------------------------------------------
 
122
 
 
123
sub post_git {
 
124
    return unless $hn;
 
125
    # nothing, and I mean NOTHING, happens if HOSTNAME is not set
 
126
    trace( 1, "post_git() on $hn" );
 
127
 
 
128
    my ( $repo, $user, $aa ) = @_[ 1, 2, 3 ];
 
129
    # we don't deal with any reads
 
130
    return if $aa eq 'R';
 
131
 
 
132
    my $sender = $rc{FROM_SERVER} || '';
 
133
    $user = '' if $sender;
 
134
 
 
135
    # ------------------------------------------------------------------
 
136
    # now you know the repo, get its mirroring details
 
137
    details($repo);
 
138
 
 
139
    trace( 1, "mirror", "post_git", $repo, "user=$user", "sender=$sender", "mode=$mode", ( $rc{REDIRECTED_PUSH} ? ("redirected") : () ) );
 
140
 
 
141
    # ------------------------------------------------------------------
 
142
    # case 1: we're master or slave, normal user pushing to us
 
143
    if ( $user and not $rc{REDIRECTED_PUSH} ) {
 
144
        trace( 3, "case 1, user push" );
 
145
        return if $mode eq 'local';
 
146
        # slave was eliminated earlier anyway, so that leaves 'master'
 
147
 
 
148
        # find all slaves and push to each of them
 
149
        push_to_slaves($repo);
 
150
 
 
151
        return;
 
152
    }
 
153
 
 
154
    # ------------------------------------------------------------------
 
155
    # case 2: we're slave, master pushing to us
 
156
    if ( $sender and not $rc{REDIRECTED_PUSH} ) {
 
157
        trace( 3, "case 2, master push" );
 
158
        # nothing to do
 
159
        return;
 
160
    }
 
161
 
 
162
    # ------------------------------------------------------------------
 
163
    # case 3: we're master, slave sending a redirected push to us
 
164
    if ( $sender and $rc{REDIRECTED_PUSH} ) {
 
165
        trace( 3, "case 2, slave redirect" );
 
166
 
 
167
        # find all slaves and push to each of them
 
168
        push_to_slaves($repo);
 
169
 
 
170
        return;
 
171
    }
 
172
}
 
173
 
 
174
{
 
175
    my $lastrepo = '';
 
176
 
 
177
    sub details {
 
178
        my $repo = shift;
 
179
        return if $lastrepo eq $repo;
 
180
 
 
181
        $master         = master($repo);
 
182
        %slaves         = slaves($repo);
 
183
        $mode           = mode($repo);
 
184
        %trusted_slaves = trusted_slaves($repo);
 
185
        trace( 3, $master, $mode, join( ",", sort keys %slaves ), join( ",", sort keys %trusted_slaves ) );
 
186
    }
 
187
 
 
188
    sub master {
 
189
        return option( +shift, 'mirror.master' );
 
190
    }
 
191
 
 
192
    sub slaves {
 
193
        my $ref = git_config( +shift, "^gitolite-options\\.mirror\\.slaves.*" );
 
194
        my %out = map { $_ => 1 } map { split } values %$ref;
 
195
        return %out;
 
196
    }
 
197
 
 
198
    sub trusted_slaves {
 
199
        my $ref = git_config( +shift, "^gitolite-options\\.mirror\\.redirectOK.*" );
 
200
        # the list of trusted slaves (where we accept redirected pushes from)
 
201
        # is either explicitly given...
 
202
        my @out = map { split } values %$ref;
 
203
        my %out = map { $_ => 1 } @out;
 
204
        # ...or it's all the slaves mentioned if the list is just a "all"
 
205
        %out = %slaves if ( @out == 1 and $out[0] eq 'all' );
 
206
        return %out;
 
207
    }
 
208
 
 
209
    sub mode {
 
210
        my $repo = shift;
 
211
        return 'local'  if not $hn;
 
212
        return 'master' if $master eq $hn;
 
213
        return 'slave'  if $slaves{$hn};
 
214
        return 'local'  if not $master and not %slaves;
 
215
        _die "$hn: '$repo' is mirrored but not here";
 
216
    }
 
217
}
 
218
 
 
219
sub push_to_slaves {
 
220
    my $repo = shift;
 
221
 
 
222
    my $u = $ENV{GL_USER};
 
223
    delete $ENV{GL_USER};    # why?  see src/commands/mirror
 
224
 
 
225
    for my $s ( sort keys %slaves ) {
 
226
        system("gitolite mirror push $s $repo &");
 
227
    }
 
228
 
 
229
    $ENV{GL_USER} = $u;
 
230
}
 
231
 
 
232
1;