From 6e721af56d70ca3b1002c8a57937ec4ee7cc10ba Mon Sep 17 00:00:00 2001 From: krimdomu Date: Sat, 10 Sep 2016 06:56:55 +0200 Subject: [PATCH] added repo locking. fixed #44 --- .perltidyrc | 5 + bin/repositorio | 9 +- lib/Rex/Repositorio.pm | 146 ++++++++++++++++++++++-------- lib/Rex/Repositorio/Log/Screen.pm | 29 ++++++ 4 files changed, 150 insertions(+), 39 deletions(-) create mode 100644 .perltidyrc create mode 100644 lib/Rex/Repositorio/Log/Screen.pm diff --git a/.perltidyrc b/.perltidyrc new file mode 100644 index 0000000..0b7e4e3 --- /dev/null +++ b/.perltidyrc @@ -0,0 +1,5 @@ +--ignore-side-comment-lengths +--indent-columns=2 +--minimum-space-to-comment=1 +--no-outdent-long-quotes +--static-side-comments diff --git a/bin/repositorio b/bin/repositorio index 6e445b6..156a617 100755 --- a/bin/repositorio +++ b/bin/repositorio @@ -15,6 +15,7 @@ use Config::General; use Log::Dispatch; use Data::Dumper; use Getopt::Long; +use Rex::Repositorio::Log::Screen; use lib '/usr/lib/rex/perl/share/perl5/vendor_perl'; @@ -58,7 +59,7 @@ my $log_file_level = $conf{LogFileLevel} || 'info'; my $screen_level = $conf{ScreenLevel} || 'info'; $screen_level = $cli{'loglevel'} if $cli{'loglevel'}; -my $log_outputs = [[ 'Screen', 'min_level' => $screen_level, stderr => 0, newline => 1, ]]; +my $log_outputs = [[ '+Rex::Repositorio::Log::Screen', 'min_level' => $screen_level, stderr => 0, newline => 1, ]]; push @{$log_outputs}, [ 'File', 'min_level' => $log_file_level, filename => $log_file, newline => 1, mode => '>>'] if $log_file; my $logger = Log::Dispatch->new(outputs => $log_outputs); @@ -68,8 +69,14 @@ $logger->debug("Configuration Dump:"); $logger->debug( Dumper( \%conf ) ); my $app = Rex::Repositorio->new( config => \%conf, logger => $logger ); +$SIG{INT} = sub { $app->exit_app(); exit(0); }; + $app->run(%cli); +END { + $app->exit_app if $app; +} + __END__ =pod diff --git a/lib/Rex/Repositorio.pm b/lib/Rex/Repositorio.pm index ace8589..b74da24 100644 --- a/lib/Rex/Repositorio.pm +++ b/lib/Rex/Repositorio.pm @@ -29,8 +29,10 @@ if ( !$Rex::Repositorio::VERSION ) { $Rex::Repositorio::VERSION = "9999.99.99"; } -has config => ( is => 'ro' ); -has logger => ( is => 'ro' ); +our @ON_EXIT; + +has config => ( is => 'ro' ); +has logger => ( is => 'ro' ); sub ua { my ( $self, %option ) = @_; @@ -55,15 +57,18 @@ sub ua { sub run { my ( $self, %option ) = @_; - # this config checking/munging stuff should probably be in the 'has config' definition? +# this config checking/munging stuff should probably be in the 'has config' definition? $self->config->{RepositoryRoot} =~ s/\/$//; - $self->logger->log_and_croak(level => 'error', message => qq/"all" is a reserved word and cannot be used as a repo name\n/) - if grep { $_ eq 'all' } keys %{ $self->config->{Repository} }; + $self->logger->log_and_croak( + level => 'error', + message => qq/"all" is a reserved word and cannot be used as a repo name\n/ + ) if grep { $_ eq 'all' } keys %{ $self->config->{Repository} }; $self->config->{TagStyle} ||= 'TopDir'; $self->logger->log_and_croak( - level => 'error', message => sprintf "Unknown TagStyle %s, must be TopDir or BottomDir\n", $self->config->{TagStyle} - ) - unless $self->config->{TagStyle} =~ m/^(?:Top|Bottom)Dir$/; + level => 'error', + message => sprintf "Unknown TagStyle %s, must be TopDir or BottomDir\n", + $self->config->{TagStyle} + ) unless $self->config->{TagStyle} =~ m/^(?:Top|Bottom)Dir$/; $self->parse_cli_option(%option); } @@ -77,13 +82,18 @@ sub parse_cli_option { } if ( exists $option{repo} ) { - $self->logger->log_and_croak(level => 'error', message => sprintf("Unknown repo: %s\n", $option{repo})) + $self->logger->log_and_croak( + level => 'error', + message => sprintf( "Unknown repo: %s\n", $option{repo} ) + ) unless $option{repo} eq 'all' - or $self->config->{Repository}->{ $option{repo} }; + or $self->config->{Repository}->{ $option{repo} }; + + $self->lock( $option{repo} ); } if ( exists $option{mirror} && exists $option{repo} ) { - $self->logger->info("Going to mirror $option{repo} This may take a while." ); + $self->logger->info("Going to mirror $option{repo} This may take a while."); my $update_files = 1; @@ -105,10 +115,10 @@ sub parse_cli_option { elsif ( exists $option{tag} && exists $option{repo} ) { $self->tag( - tag => $option{tag}, + tag => $option{tag}, clonetag => $option{clonetag} || 'head', - repo => $option{repo}, - force => $option{force} || 0, + repo => $option{repo}, + force => $option{force} || 0, ); } @@ -147,7 +157,10 @@ sub parse_cli_option { } elsif ( exists $option{"remove-file"} && exists $option{repo} ) { - $self->remove_file( file => $option{"remove-file"}, repo => $option{repo} ); + $self->remove_file( + file => $option{"remove-file"}, + repo => $option{repo} + ); } else { @@ -156,6 +169,43 @@ sub parse_cli_option { } } +sub lock { + my $self = shift; + my $repo = shift; + + open( my $fh, ">", "/tmp/repositorio.$repo.lock" ); + my $flock_ok = flock( $fh, 2 ); + while ( !$flock_ok ) { + $self->logger->info("Waiting for repository lock $repo"); + sleep 1; + $flock_ok = flock( $fh, 2 ); + } + + $self->on_exit( + sub { + close $fh; + } + ); +} + +sub on_exit { + my $self = shift; + my $code = shift; + push @ON_EXIT, $code; +} + +sub exit_app { + my $self = shift; + + $self->logger->debug("Running exit code."); + + for my $e (@ON_EXIT) { + $e->(); + } + + $self->logger->debug("Exit code finished."); +} + sub server { my $self = shift; my %option = validate( @@ -441,34 +491,47 @@ sub tag { # Why is this an array? my @dirs; - push @dirs, $self->get_repo_dir(repo => $option{repo}, tag => $option{clonetag}); - my $tag_dir = $self->get_repo_dir(repo => $option{repo}, tag => $option{tag}); + push @dirs, + $self->get_repo_dir( repo => $option{repo}, tag => $option{clonetag} ); + my $tag_dir = + $self->get_repo_dir( repo => $option{repo}, tag => $option{tag} ); - $self->logger->log_and_croak(level => 'error', message => "Unknown tag $option{clonetag} on repo $option{repo} ($dirs[0])\n") - unless ( -d $dirs[0] ); + $self->logger->log_and_croak( + level => 'error', + message => + "Unknown tag $option{clonetag} on repo $option{repo} ($dirs[0])\n" + ) unless ( -d $dirs[0] ); if ( -e $tag_dir ) { - if( $option{force} ) { + if ( $option{force} ) { $self->logger->debug("Removing $tag_dir"); rmtree $tag_dir; # should be remove_tree, but will use legacy to match mkdir } else { - $self->logger->log_and_croak(level => 'error', message => "Tag $option{tag} on repo $option{repo} already exists (${tag_dir}), use --force\n"); + $self->logger->log_and_croak( + level => 'error', + message => + "Tag $option{tag} on repo $option{repo} already exists (${tag_dir}), use --force\n" + ); } } make_path($tag_dir); for my $dir (@dirs) { - opendir my $dh, $dir - or $self->logger->log_and_croak(level => 'error', message => "Failed to open $dir: $!\nNew tag is probably unusable\n"); + opendir my $dh, + $dir + or $self->logger->log_and_croak( + level => 'error', + message => "Failed to open $dir: $!\nNew tag is probably unusable\n" + ); while ( my $entry = readdir $dh ) { next if ( $entry eq '.' || $entry eq '..' ); - my $rel_entry = File::Spec->catfile($dir, $entry); + my $rel_entry = File::Spec->catfile( $dir, $entry ); $rel_entry =~ s{^$dirs[0]/}{}; # TODO use File::Spec? - my $srcfile = File::Spec->catfile($dir,$entry); - my $dstfile = File::Spec->catfile($tag_dir,$rel_entry); + my $srcfile = File::Spec->catfile( $dir, $entry ); + my $dstfile = File::Spec->catfile( $tag_dir, $rel_entry ); $self->logger->debug("Tag Src: $srcfile, Dst: $dstfile"); if ( -d $srcfile ) { @@ -478,8 +541,7 @@ sub tag { next; } - $self->logger->debug( - "Linking (hard): $srcfile -> $dstfile"); + $self->logger->debug("Linking (hard): $srcfile -> $dstfile"); link $srcfile, $dstfile; } closedir $dh; @@ -500,25 +562,28 @@ sub get_errata_dir { } ); - if ($self->config->{TagStyle} eq 'TopDir') { + if ( $self->config->{TagStyle} eq 'TopDir' ) { return File::Spec->catdir( File::Spec->rel2abs( $self->config->{RepositoryRoot} ), $option{tag}, $option{repo}, 'errata' ); } - elsif ($self->config->{TagStyle} eq 'BottomDir') { + elsif ( $self->config->{TagStyle} eq 'BottomDir' ) { return File::Spec->catdir( File::Spec->rel2abs( $self->config->{RepositoryRoot} ), $option{repo}, $option{tag}, 'errata' ); } else { # add other styles here - $self->logger->log_and_croak(level => 'error', message => 'Shouldnt have gotten here'); + $self->logger->log_and_croak( + level => 'error', + message => 'Shouldnt have gotten here' + ); } } sub get_repo_dir { - my $self = shift; + my $self = shift; my %option = validate( @_, { @@ -526,7 +591,7 @@ sub get_repo_dir { type => SCALAR }, tag => { - type => SCALAR, + type => SCALAR, default => 'head', } } @@ -534,14 +599,19 @@ sub get_repo_dir { my $repo_config = $self->config->{Repository}->{ $option{repo} }; - if ($self->config->{TagStyle} eq 'TopDir') { - return File::Spec->catdir($self->config->{RepositoryRoot}, $option{tag}, $repo_config->{local}); + if ( $self->config->{TagStyle} eq 'TopDir' ) { + return File::Spec->catdir( $self->config->{RepositoryRoot}, + $option{tag}, $repo_config->{local} ); } - elsif ($self->config->{TagStyle} eq 'BottomDir') { - return File::Spec->catdir($self->config->{RepositoryRoot}, $repo_config->{local}, $option{tag}); + elsif ( $self->config->{TagStyle} eq 'BottomDir' ) { + return File::Spec->catdir( $self->config->{RepositoryRoot}, + $repo_config->{local}, $option{tag} ); } else { - $self->logger->log_and_croak(level => 'error', message => 'get_repo_dir: Unknown TagStyle: '.$self->config->{TagStyle}); + $self->logger->log_and_croak( + level => 'error', + message => 'get_repo_dir: Unknown TagStyle: ' . $self->config->{TagStyle} + ); } } diff --git a/lib/Rex/Repositorio/Log/Screen.pm b/lib/Rex/Repositorio/Log/Screen.pm new file mode 100644 index 0000000..dc29a6d --- /dev/null +++ b/lib/Rex/Repositorio/Log/Screen.pm @@ -0,0 +1,29 @@ +package Rex::Repositorio::Log::Screen; + +use base qw(Log::Dispatch::Screen); +use DateTime; + +sub new { + my $proto = shift; + return $proto->SUPER::new(@_); +} + +sub log_message { + my $self = shift; + my %p = @_; + + my $dt = DateTime->now; + + my $message = + $self->{utf8} ? encode( 'UTF-8', $p{message} ) : $p{message}; + if ( $self->{stderr} ) { + print STDERR $dt->iso8601() . $dt->strftime('%Z') . " "; + print STDERR $message; + } + else { + print STDOUT $dt->iso8601() . $dt->strftime('%Z') . " "; + print STDOUT $message; + } +} + +1;