#!/usr/bin/perl

# Run puppet code-depoy for that all repositories too far behind their
# live directory couotnerparts..

## no critic (CodeLayout::ProhibitParensWithBuiltins) ;

use strict ;
use warnings ;
use autodie ;

use Carp ;
use Data::Dumper ;
use Getopt::Long::Descriptive ;
use Sys::Syslog qw(:standard :macros) ;

use Stanford::Puppet::RepoInfo ;
use Stanford::Orange::Util qw/ trim run_command_improved / ;

my $VERBOSE ;
my $SYNC_SECS_LIMIT ;

### ######## ######## ######## ######## ######## ######## ######## ######## #####
sub deploy_code {
    my ($repo) = @_ ;

    my $start_time = time() ;
    my @cmd = ('puppet', 'code', 'deploy', $repo, '--wait') ;
    progress(q{about to run command '} . join(q{ }, @cmd) . q{'}) ;

    my ($stdout, $stderr, $rc) = run_command_improved(@cmd) ;

    my $end_time = time() ;
    my $elapsed_seconds = $end_time - $start_time ;
    progress("code deploy for repo $repo took $elapsed_seconds seconds") ;
    
    if ($stderr) {
        my $msg = "error running code deploy: ($stdout, $stderr)" ;
        progress($msg) ;
        logme($msg) ;
        return 0 ;
    } else {
        return 1 ;
    }
}

sub progress {
    my ($msg) = @_ ;

    if ($VERBOSE) {
        print "progress: $msg\n" ;
    }
    return ;
}

sub message {
    my ($msg) = @_ ;
    print "$msg\n" ;
    return ;
}

sub logme {
    my ($msg) = @_ ;
    openlog('puppet-repo-deploy', 'nowait', LOG_LOCAL0) ;
    syslog(LOG_INFO, $msg) ;
    closelog() ;
    return ;
}

### ######## ######## ######## ######## ######## ######## ######## ######## #####

#<<<  perltiudy ignore this section as I like the options formatted this way.
my ($opt, $usage) = describe_options(
    'puppet-repo-deploy %o',
    [ 'seconds-behind-limit|s=s',
                      're-deploy any repo more than this many seconds behind', { default => 5 * 60 } ],
    [ 'verbose|v',    'print extra stuff'            ],
    [ 'help|h',       'print usage message and exit', {'shortcircuit' => 1}],
    );
#>>>

#if ($opt->manual) {
#    pod2usage(-verbose => 2) ;
#    exit 0 ;
#}

if ($opt->help()) {
    print $usage->text ;
    exit ;
}

$VERBOSE         = $opt->verbose() ;
$SYNC_SECS_LIMIT = $opt->seconds_behind_limit() ;

progress("seconds-behind-limit: $SYNC_SECS_LIMIT") ;

my $ri = Stanford::Puppet::RepoInfo->new(
    'regex'         => q{.},
    'output-format' => 'date-only',
    'sync-seconds'  => 1,
    'verbose'       => $VERBOSE,
) ;


my @messages = $ri->last_commits() ;

# Parse messages
my @repos = () ;
foreach my $message (@messages) {
    if ($message =~ m{^([^:]+):\s*(\d+)$}xsm) {
        my ($repo, $secs_out_of_sync) = (trim($1), $2 + 0) ;
        push(@repos, [$repo, $secs_out_of_sync]) ;
    } else {
        croak "cannot parse message '$message'" ;
    }
}

# Sort the repos by secs_out_of_sync
my @repos_sorted = reverse sort { $a->[1] <=> $b->[1] } @repos ;

# Go through the list and any repo that has a secs_out_of_sync greater than
# the limit we have set do a "code deploy".
foreach my $repo (@repos_sorted) {
    my ($repo, $secs_out_of_sync) = @{$repo} ;

    if ($secs_out_of_sync > $SYNC_SECS_LIMIT) {
        my $msg =
          "repo $repo is out-of-sync ($secs_out_of_sync > $SYNC_SECS_LIMIT)" ;
        progress($msg) ;
        message($msg) ;
        logme($msg) ;
        if (deploy_code($repo)) {
            $msg = "re-deployed repo $repo" ;
        } else {
            $msg = "failed to re-deploy repo $repo" ;
        }
        progress($msg) ;
        message($msg) ;
        logme($msg) ;
    } else {
        progress("repo $repo is not out-of-sync "
              . "($secs_out_of_sync <= $SYNC_SECS_LIMIT)") ;
    }

}

exit 0 ;


__END__

=head1 NAME

puppet-repo-deploy - Run Puppet code deploy on out-of-sync git repos

=head1 SYNOPSIS

puppet-repo-deploy [options]

=head1 DESCRIPTION

The B<puppet-repo-deploy> script finds all Puppet Git repositories that
are out-of-sync with respect to the Puppet Enterprise "live" directories
and for these repositories run Puppet code deploy. This script will log an
informational message to the syslog for any repos it does a code deploy
on.

Too out-of-sync is defined as more than 300 seconds behind, but this
parameter can be set using the B<--seconds-behind-limit> option.

=head1 OPTIONS

=over 4

=item B<--seconds-behind-limit>=NUM_SECONDS

If a Puppet GIT repository is more than B<NUM_SECONDS> behind its live
directory counterpart run Puppet code deploy on this repository. The default
value for B<--seconds-behind-limit> is 300 seconds (five minutes).

=item B<-h|--help>

Show a brief text.

=item B<-v|--verbose>

Show debugging information.


=back

=head1 EXIT STATUS

The script will exit with 0 if the script completes and there were no
failures, 1 for any other reason.

=head1 SEE ALSO

git-info(1)

=head1 AUTHOR

Adam Lewenberg <adamhl@stanford.edu>

=head1 COPYRIGHT AND LICENSE

Copyright 2020 The Board of Trustees of the Leland Stanford Junior
University.  All rights reserved.

Permission to use, copy, modify, and distribute this software and its
documentation for any purpose and without fee is hereby granted, provided
that the above copyright notice appear in all copies and that both that
copyright notice and this permission notice appear in supporting
documentation, and that the name of Stanford University not be used in
advertising or publicity pertaining to distribution of the software
without specific, written prior permission.  Stanford University makes no
representations about the suitability of this software for any purpose.
It is provided "as is" without express or implied warranty.

THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.

=cut

#############################################################################
#############################################################################
