#=============================================================================
# Doloforge::Service::Mason : configure Apache-perl (Mason back-end) for any client/project
#
# Generate config files from templates, add a config file to the whole,
# reload Apache (well, not yet)
#
# Full help :
#   perldoc Doloforge::Service::Mason
#=============================================================================


package Doloforge::Service::Mason;

use strict;

# This base class implements the constructor and help methods in a generic way.
# This class is specifically a derivative of another service class, since they
# are both based on Apache and need the same kind of configuration.
use base qw(Doloforge::Service::Apache);


#=============================================================================
=encoding utf8

=head1 NAME

Doloforge::Service::Mason : Configure Apache-perl (Mason back-end) for any client/project

=head1 SYNOPSIS

    # Update the full configuration, running each client/project/service update
    doloforge config --update

=head1 DESCRIPTION

Todo

=cut


#=============================================================================
# Nearly everything is inherited from Doloforge::Service::Apache !!!
#
# Service-related directories are created by the PerlRequire'd script, run
# by Apache at startup. This script is located in /etc/doloforge/ directory.


#=============================================================================
# Method update
#
# Update the service configuration on the host, depending on
# client/project/service config files...
# This method receives :
#   option: context options passesd on the command-line (quite useless there)
#   client_conf: client configuration file contents (client.$name.yaml)
#   project_conf: project configuration file contents (project.$name.client.$name.yaml)

sub update {
    my $self = shift;

    my ($option, $client_conf, $project_conf) = @_;

    # Call the same method from the super-class first, which will do generic Apache things.
    $self->SUPER::update($option, $client_conf, $project_conf);

    # Link the project's DocumentRoot in Mason's central ComponentRoot, just like:
    #   ln -s /srv/doloforge/shared/devl/internal/sandbox/mason/htdocs /srv/doloforge/shared/devl/.doloforge/mason/components/internal_sandbox
    # Create link on NFS servers only, since everything is in shared storage...
    $self->link_file(
        "$self->{global}->{dir}->{shared}/$client_conf->{name}/$project_conf->{name}/$self->{name}/htdocs",
        "$self->{global}->{dir}->{shared}/.doloforge/$self->{name}/components/$client_conf->{name}_$project_conf->{name}",
    ) if ($self->has_role('nfs_role'));
}


#=============================================================================
# Method maintenance_on_appserv
#
# Additionnal maintenance tasks, added to the basic Apache base class one.
# These tasks are related to Mason session and cache file (remove oldest ones).

sub maintenance_on_appserv {
    my $self = shift;

    # Call the base class maintenance method first
    my ($interval) = @_;
    $self->SUPER::maintenance_on_appserv(@_);

    # Then do the specifics : remove the old session lock and cache files on application servers
    #   find /var/lib/doloforge/mason/sessions-lock/ -type f -mtime +0 | xargs rm
    # "find -mtime +0" will find files which last modification time was earlier than 24h ago,
    # which is more than the standard Mason session duration (+12h).
    # Verbose/dry-run mode : "xargs -0 --no-run-if-empty --verbose echo rm" instead of "xargs -0 --no-run-if-empty rm"
    # Take care of the size of the verbose output, which can easily rise to multi-MB, which is higher than the maximum permited email message size.
    my $session_dir = "$self->{global}->{dir}->{varlib}/mason/sessions";
    my $session_lck = "$self->{global}->{dir}->{varlib}/mason/sessions-lock";
    my $cache_dir = "$self->{global}->{dir}->{varcache}/mason/data-$self->{global}->{stage}/cache";
    my $remove_session_days = $self->{config}->{maintenance}->{remove_session_days} or '+1';
    my $remove_session_mins = $self->{config}->{maintenance}->{remove_session_mins} or '+1440';
    my $remove_session_args = ($interval eq 'hourly' ? "-mmin $remove_session_mins" : "-mtime $remove_session_days");
    my $warn_session_size = $self->{config}->{maintenance}->{warn_session_size} or '+99k';
    my $remove_cache_days = $self->{config}->{maintenance}->{remove_cache_days} or '+1';
    my $remove_cache_mins = $self->{config}->{maintenance}->{remove_cache_mins} or '+1440';
    my $remove_cache_args = ($interval eq 'hourly' ? "-mmin $remove_cache_mins" : "-mtime $remove_cache_days");

    # The directory may not already exist, if apache-perl was not yet started
    if (-d $session_dir or -d $session_lck or -d $cache_dir) {
        $self->run_sub(
            "$self->{name} removing expired session files in $session_dir, locks in $session_lck and data cache in $cache_dir",
            (getpwnam($self->{config}->{apacheperl_uid} or 'www-data'))[2,3],
            sub {
                # Remove expired session data files
                system("find $session_dir -type f $remove_session_args \\( -size $warn_session_size -fls /dev/stderr -o -print0 \\) | xargs -0 --no-run-if-empty rm")
                    if -d $session_dir;

                # Remove expired session locks
                system("find $session_lck -type f $remove_session_args -print0 | xargs -0 --no-run-if-empty rm")
                    if -d $session_lck;

                # Remove expired cache files and remaining empty directories (except the base cache dir itself)
                # Do not fail on dirs that were re-filled by live Mason between the find -empty and rmdir execution time
                if (-d $cache_dir) {
                    system("find $cache_dir -type f $remove_cache_args -print0 | xargs -0 --no-run-if-empty rm");
                    system("find $cache_dir -depth -type d -empty ! -path $cache_dir -print0 | xargs -0 --no-run-if-empty rmdir --ignore-fail-on-non-empty");
                }
            }
        );
    }
}

sub maintenance_on_nfs {
    my $self = shift;

    # Call the base class maintenance method first
    my ($interval) = @_;
    $self->SUPER::maintenance_on_nfs(@_);

    # Then do the specifics : remove the old session data files on the shared NFS server
    #   find /srv/doloforge/shared/.doloforge/mason/sessions/ -type f -mtime +0 | xargs rm
    # File larger than an arbitrary limit are kept for analysis, as they may reveal a problem,
    # and their details are output to stderr, ie. is sent via email to the administrators.
    # Cache dir may or may not be also stored on the NFS server.
    my $session_dir = "$self->{global}->{dir}->{shared}/.doloforge/mason/sessions";
    my $cache_dir = "$self->{global}->{dir}->{shared}/.doloforge/mason/data/cache";
    my $remove_session_days = $self->{config}->{maintenance}->{remove_session_days} or '+1';
    my $remove_session_mins = $self->{config}->{maintenance}->{remove_session_mins} or '+1440';
    my $remove_session_args = ($interval eq 'hourly' ? "-mmin $remove_session_mins" : "-mtime $remove_session_days");
    my $warn_session_size = $self->{config}->{maintenance}->{warn_session_size} or '+99k';
    my $remove_cache_days = $self->{config}->{maintenance}->{remove_cache_days} or '+1';
    my $remove_cache_mins = $self->{config}->{maintenance}->{remove_cache_mins} or '+1440';
    my $remove_cache_args = ($interval eq 'hourly' ? "-mmin $remove_cache_mins" : "-mtime $remove_cache_days");

    # The directory may not already exist, if apache-perl was not yet started
    if (-d $session_dir or -d $cache_dir) {
        $self->run_sub(
            "$self->{name} removing expired session data in $session_dir and data cache in $cache_dir",
            (getpwnam($self->{config}->{apacheperl_uid} or 'www-data'))[2,3],
            sub {
                # Remove expired session data files
                system("find $session_dir -type f $remove_session_args \\( -size $warn_session_size -fls /dev/stderr -o -print0 \\) | xargs -0 --no-run-if-empty rm")
                    if -d $session_dir;

                # Remove expired cache files and remaining empty directories (except the base cache dir itself)
                # Do not fail on dirs that were re-filled by live Mason between the find -empty and rmdir execution time
                if (-d $cache_dir) {
                    system("find $cache_dir -type f $remove_cache_args -print0 | xargs -0 --no-run-if-empty rm");
                    system("find $cache_dir -depth -type d -empty ! -path $cache_dir -print0 | xargs -0 --no-run-if-empty rmdir --ignore-fail-on-non-empty");
                }
            }
        );
    }
}




1;
__END__

=head1 AUTHOR

Nicolas Huillard <F<nicolas@huillard.net>>, Dolomède

=head1 SEE ALSO

doloforge(1)

=cut
