App::ForExample - A guide through Catalyst, Apache, lighttpd, nginx, monit, ..., configuration hell


App-ForExample documentation Contained in the App-ForExample distribution.

Index


Code Index:

NAME

Top

App::ForExample - A guide through Catalyst, Apache, lighttpd, nginx, monit, ..., configuration hell

VERSION

Top

Version 0.024

SYNOPSIS

Top

    # To output a FastCGI (ExternalServer)/Apache configuration (with monit stub and start-stop script), run:
    for-example catalyst/fastcgi apache2 standalone --class My::App --hostname example.com --output my-app

    # The above command would have created the following:

        my-app.apache2      The Apache2 virtual host configuration (hosted at (www.)example.com)
        my-app.start-stop   The start/stop script to launch the FastCGI process
        my-app.monit        A monit stub used for monitoring the FastCGI process

    # This will generate a basic, stripped-down monit configuration (monitrc) suitable for a non-root user:
    for-example monit --home $HOME/monit --output $HOME/monit/monitrc

    # A mod_perl configuration for Catalyst:
    for-example catalyst/mod_perl --class Project::Xyzzy --hostname xyzzy.com --home Project-Xyzzy

DESCRIPTION

Top

App::ForExample is a command-line tool for generating sample configurations. It is not designed to do configuration management, but rather as a guide to get you 80% of the way there

Besides the usual Apache, lighttpd, nginx, and FastCGI configurations, App::ForExample can create a FastCGI start-stop script and a monit configuration for monitoring those processes

USAGE

Top

    Usage: for-example ACTION

    Where ACTION can be

    (Note: Every option below is, well, optional. If not specified, a fun default will be chosen/guessed for you)

        catalyst/fastcgi ...

            Generate a Catalyst FastCGI configuration (for monit, start-stop, or the specified http daemon and fastcgi method)

            --class             The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
            --home              The path to your Catalyst home directory, default: . (The current directory)
            --log-home          The directory to log into, default: <home>/log (Below the directory given by --home)
            --base              The base for your application, default: / (At the root)
            --hostname          The hostname from which your application is served (e.g. example.com)

            --bare              Do not output anything BUT the configuration (no monit, no start-stop)
            --output -          Print output to stdout
            --output <path>     Write output to <path> (which can be either a directory or file)
                                This will split output appropiately (e.g. <file>.apache2, <file>.start-stop, <file>.monit)

            --fastcgi-script                The <path> to the Catalyst fastcgi script (e.g. script/xyzzy_fastcgi.pl)
            --fastcgi-socket <path>         Have fastcgi use <path> for the file socket
            --fastcgi-socket <host:port>    Have fastcgi use <host:port> for the socket
            --fastcgi-pid-file <path>       Store the pid for the process in <path>

            apache2 standalone  Apache2 with standalone FastCGI (mod_fastcgi)
            apache2 static      Apache2 with static FastCGI (mod_fastcgi)
            apache2 dynamic     Apache2 with dynamic FastCGI (mod_fastcgi)

            lighttpd standalone lighttpd with dynamic FastCGI
            lighttpd static     lighttpd with static FastCGI

            nginx               nginx with standalone FastCGI (the only kind supported)

            monit               A monit configuration for a standalone FastCGI setup
            start-stop          A start-stop script for a standalone FastCGI setup

        catalyst/mod_perl

            Generate a mod_perl2 (for Apache2) Catalyst configuration

            --class             The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
            --home              The path to your Catalyst home directory, default: . (The current directory)
            --log-home          The directory to log into, default: <home>/log (Below the directory given by --home)
            --base              The base for your application, default: / (At the root)
            --hostname          The hostname from which your application is served (e.g. example.com)

        monit

            Generate a basic, stripped-down monit configuration suitable for a non-root user

            --home              The directory designated monit home (containing the pid file, log, rc, ...)

TUTORIAL

Top

Apache2 with FastCGI on Ubuntu

Install apache2, mod_fastcgi, and FCGI

    sudo apt-get install apache2 libapache2-mod-fastcgi

    cpan -i FCGI

Create the Catalyst application My::App

    catalyst.pl My::App

Use App::ForExample to generate the configuration

    cd My-App
    for-example catalyst/fastcgi apache2 standalone --class My::App --hostname my-app.localhost --output my-app

Make the log directory

    mkdir log

Install the apache2 configuration

    sudo cp my-app.apache2 /etc/apache2/sites-enabled

Enable the fastcgi start-stop script (with execute permissions)

    chmod +x my-app.start-stop

Add a my-app.localhost entry to /etc/hosts

    127.0.0.1       my-app.localhost

Start your application

    ./my-app.start-stop start
    sudo /etc/init.d/apache restart

Visit your application at http://my-app.localhost

INSTALL

Top

You can install App::ForExample by using CPAN:

    cpan -i App::ForExample

If that doesn't work properly, you can find help at:

    http://sial.org/howto/perl/life-with-cpan/
    http://sial.org/howto/perl/life-with-cpan/macosx/ # Help on Mac OS X
    http://sial.org/howto/perl/life-with-cpan/non-root/ # Help with a non-root account

CONTRIBUTE

Top

You can contribute or fork this project via GitHub:

http://github.com/robertkrimen/App-ForExample/tree/master

    git clone git://github.com/robertkrimen/App-ForExample.git

SEE ALSO

Top

http://dev.catalystframework.org/wiki/deployment

Catalyst::Engine::Apache

Catalyst::Engine::FastCGI

ACKNOWLEDGEMENTS

Top

All the people that have put effort into the Catalyst documentation, including the pod, advent, and wiki

Dan Dascalescu, Jay Shirley, Tomas Doran, Daniel Austin, Jason Felds, Moritz Onken, and Brian Friday, who all put effort into the deployment wiki, which formed the basis for many parts of this tool

AUTHOR

Top

Robert Krimen, <rkrimen at cpan.org>

BUGS

Top

Please report any bugs or feature requests to bug-app-forexample at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=App-ForExample. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

Top

You can find documentation for this module with the perldoc command.

    perldoc App::ForExample




You can also look for information at:

* RT: CPAN's request tracker

http://rt.cpan.org/NoAuth/Bugs.html?Dist=App-ForExample

* AnnoCPAN: Annotated CPAN documentation

http://annocpan.org/dist/App-ForExample

* CPAN Ratings

http://cpanratings.perl.org/d/App-ForExample

* Search CPAN

http://search.cpan.org/dist/App-ForExample/

ACKNOWLEDGEMENTS

Top

COPYRIGHT & LICENSE

Top


App-ForExample documentation Contained in the App-ForExample distribution.
package App::ForExample;

use warnings;
use strict;

our $VERSION = '0.024';

use App::ForExample::Catalog;

use Template;
use Carp;
use Path::Class;

my $catalog = App::ForExample::Catalog->catalog;
my $tt = Template->new({ BLOCKS => $catalog->{common} });

sub process ($@) {
    my $given = shift;

    my ($template);
    if ( ref $given eq 'SCALAR' ) {
        $template = $given;
    }
    else {
        $template = $catalog->{$given} or croak "Template \"$given\" does not exist in the catalog";
    }

    my $output;
    $tt->process( $template => { @_ }, \$output ) or croak "Error processing template \"$given\": ", $tt->error; 
    return $output;
}

sub output ($@) {
    my $ctx = shift;

    my $to = $ctx->option( 'output' );
    $to = '-' unless defined $to && length $to;

    if ( $to eq '-' ) {
        my $ii = 0;
        while ( @_ ) {
            shift;
            print "---\n" if $ii++ > 0;
            print shift;
        }
    }
    else {
        my $name_hint = shift;

        if ( $name_hint =~ m/^(.*)\.(.*)$/ ) { # E.g. catalyst-fastcgi.apache or .lighttpd
            $name_hint = $1;
            unshift @_, $2;
        }

        if ( ! -f $to && ( $to =~ m/\/$/ || -d _ ) ) {
            $to = file( $to, $name_hint );
        }
        else {
            $to = file $to;
        }

        my $parent = $to->parent;
        $parent->mkpath unless -d $parent;

        while ( @_ ) {
            my $name = shift;
            my $content = shift;

            my $file = file( join '.', $to, $name );
            $file->openw->print( $content );
            print "Made $file\n";
        }
    }
}

use Getopt::Chain::Declare;

sub package2name ($) {
    my $package = shift;
    my $name = $package;
    $name =~ s/::/-/g;
    $name = lc $name;
    my $name_underscore = $name;
    $name_underscore =~ s/-/_/g;
    return ( $name, $name_underscore );
}

my @parse_catalyst = qw/ package|class=s name=s home=s log-home=s base=s hostname=s fastcgi-script=s fastcgi-socket=s fastcgi-socket-path=s fastcgi-pid-file=s/;
sub parse_catalyst ($) {
    my $ctx = shift;

    # Catalyst package
    my $package = $ctx->option( 'package' ) || 'Project::Xyzzy';
    my ($package_name, $name_underscore) = package2name $package;

    # Catalyst name
    my $name = $ctx->option( 'name' );
    $name = $package_name unless defined $name;

    # Catalyst home
    my $home = $ctx->option( 'home' ) || "./";
    $home = dir( $home )->absolute;

    my $log_home = $ctx->option( 'log_home' ) || $home->subdir( 'log' );
    $log_home = dir( $log_home )->absolute;

    # Catalyst application base
    my $base = $ctx->option( 'base' ) || '/';
    $base =~ s/^\/+//;
    my $alias_base = $base eq '' ? '/' : "/$base/";

    # Hostname
    my $hostname = $ctx->option( 'hostname' ) || "$name.example.com";

    my $fastcgi_script = $ctx->option( 'fastcgi-script' );
    $fastcgi_script = join '/', $home, 'script', "${name_underscore}_fastcgi.pl" unless defined $fastcgi_script;
    my $fastcgi_script_basename = file( $fastcgi_script )->basename;
    my $fastcgi_socket = $ctx->option( 'fastcgi-socket' );
    $fastcgi_socket = "/tmp/$name.socket" unless defined $fastcgi_socket;
    my $fastcgi_host_port;
    if ( $fastcgi_socket =~ m/^(.+):(\d+)$/ ) {
        $fastcgi_host_port = [ $1, $2 ];
    }
    my $fastcgi_socket_path = $ctx->option( 'fastcgi-socket-path' );
    $fastcgi_socket_path = "/tmp/$name.fcgi" unless defined $fastcgi_socket_path;
    my $fastcgi_pid_file = $ctx->option( 'fastcgi-pid-file' );
    $fastcgi_pid_file = "$name-fastcgi.pid" unless $fastcgi_pid_file;
    $fastcgi_pid_file = join '/', $home, $fastcgi_pid_file unless $fastcgi_pid_file =~ m/^\//;

    my @data;
    push @data, package => $package,
        name => $name,
        name_underscore => $name_underscore,
        home => $home,
        log_home => $log_home,
        base => $base,
        alias_base => $alias_base,
        hostname => $hostname,
        fastcgi_script => $fastcgi_script,
        fastcgi_script_basename => $fastcgi_script_basename,
        fastcgi_socket => $fastcgi_socket,
        fastcgi_host_port => $fastcgi_host_port,
        fastcgi_socket_path => $fastcgi_socket_path,
        fastcgi_pid_file => $fastcgi_pid_file,
    ;
    return { @data };
}

sub do_help ($) {
    my $ctx = shift;

    print <<_END_;
Usage: for-example ACTION

Where ACTION can be

(Note: Every option below is, well, optional. If not specified, a fun default will be chosen/guessed for you)

    catalyst/fastcgi ...

        Generate a Catalyst FastCGI configuration (for monit, start-stop, or the specified http daemon and fastcgi method)

        --class             The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
        --home              The path to your Catalyst home directory, default: . (The current directory)
        --log-home          The directory to log into, default: <home>/log (Below the directory given by --home)
        --base              The base for your application, default: / (At the root)
        --hostname          The hostname from which your application is served (e.g. example.com)

        --bare              Do not output anything BUT the configuration (no monit, no start-stop)
        --output -          Print output to stdout
        --output <path>     Write output to <path> (which can be either a directory or file)
                            This will split output appropiately (e.g. <file>.apache2, <file>.start-stop, <file>.monit)

        --fastcgi-script                The <path> to the Catalyst fastcgi script (e.g. script/xyzzy_fastcgi.pl)
        --fastcgi-socket <path>         Have fastcgi use <path> for the file socket
        --fastcgi-socket <host:port>    Have fastcgi use <host:port> for the socket
        --fastcgi-pid-file <path>       Store the pid for the process in <path>

        apache2 standalone  Apache2 with standalone FastCGI (mod_fastcgi)
        apache2 static      Apache2 with static FastCGI (mod_fastcgi)
        apache2 dynamic     Apache2 with dynamic FastCGI (mod_fastcgi)

        lighttpd standalone lighttpd with dynamic FastCGI
        lighttpd static     lighttpd with static FastCGI

        nginx               nginx with standalone FastCGI (the only kind supported)

        monit               A monit configuration for a standalone FastCGI setup
        start-stop          A start-stop script for a standalone FastCGI setup
        
    catalyst/mod_perl

        Generate a mod_perl2 (for Apache2) Catalyst configuration

        --class             The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
        --home              The path to your Catalyst home directory, default: . (The current directory)
        --log-home          The directory to log into, default: <home>/log (Below the directory given by --home)
        --base              The base for your application, default: / (At the root)
        --hostname          The hostname from which your application is served (e.g. example.com)

    monit

        Generate a basic, stripped-down monit configuration suitable for a non-root user

        --home              The directory designated monit home (containing the pid file, log, rc, ...)

For example:

    for-example catalyst/fastcgi apache2 standalone --class My::App --hostname example.com
    for-example monit --home \$HOME/my-monit
    for-example catalyst/mod_perl --class Project::Xyzzy --hostname xyzzy.com --home Project-Xyzzy

_END_
}

start [qw/ help|h|? /], sub {
    my $ctx = shift;

    if ( $ctx->option( 'help' ) || $ctx->last ) {
        do_help $ctx;
        exit 0;
    }
};

rewrite qr#catalyst/(?:mod_perl[12]|modperl[12]?)# => 'catalyst/mod_perl';

on 'catalyst/mod_perl *' => 
    [ qw/ output=s /, @parse_catalyst ] => sub {
    my $ctx = shift;
    
    my ($server);
    for ( @_ ) {
        m/(apache2?)/ and ($server) = ($1) or

        croak "Don't understand argument $_ (@_)";
    }
    ($server) = qw/apache2/;

    my @data;
    my $catalyst_data = parse_catalyst $ctx;
    push @data, %$catalyst_data;

    if ( $server =~ m/^apache2?$/ ) {
        output( $ctx, 'catalyst-mod_perl' => process 'catalyst/mod_perl/apache2' => @data );
    }
    else {
        croak "Don't understand server \"$server\""
    }

};

on 'catalyst/fastcgi *' => 
    [ @parse_catalyst, qw/ bare output=s /] => sub {
    my $ctx = shift;
    
    my ($server, $server_module, $mode);
    for ( @_ ) {
        m/(apache2?)(?:=(?:mod_)?(fastcgi|fcgid))?/ and ($server, $server_module) = ($1, $2) or
        m/lighttpd/ and $server = 'lighttpd' or
        m/nginx/ and $server = 'nginx' or
        m/(monit|start-stop)/ and $server = $1 or # Not really a server, but...

        m/standalone/ and $mode = 'standalone' or
        m/static/ and $mode = 'static' or
        m/dynamic/ and $mode = 'dynamic' or

        croak "Don't understand argument $_ (@_)";
    }

    ($server, $server_module) = qw/apache2 fastcgi/ unless $server;
    ($mode) = qw/standalone/ unless $mode;

    my @data;

    my $bare = $ctx->option( 'bare' );

    my $catalyst_data = parse_catalyst $ctx;
    push @data, %$catalyst_data;
    my $name = $catalyst_data->{name};

    if ( $server =~ m/^apache2?$/ ) {

        if ( $mode eq 'standalone' ) {
            # TODO Error in Catalyst::Engine::FastCGI dox?
            my @output;
            push @output, 'catalyst-fastcgi.apache2' => process 'catalyst/fastcgi/apache2/standalone' => @data;
            unless ($bare) {
                push @output, 'start-stop' => process 'catalyst/fastcgi/start-stop' => @data;
                push @output, 'monit' => process 'catalyst/fastcgi/monit' => @data;
            }
            output( $ctx, @output );
        }
        elsif ( $mode eq 'dynamic' ) {
            output( $ctx, 'catalyst-fastcgi.apache2' => process 'catalyst/fastcgi/apache2/dynamic' => @data );
        }
        elsif ( $mode eq 'static' ) {
            output( $ctx, 'catalyst-fastcgi.apache2' => process 'catalyst/fastcgi/apache2/static' => @data );
        }
        else {
            croak "Don't understand mode \"$mode\""
        }
    }
    elsif ( $server eq 'lighttpd' ) {

        if ( $mode eq 'standalone' ) {
            my @output;
            push @output, 'catalyst-fastcgi.lighttpd' => process 'catalyst/fastcgi/lighttpd/standalone' => @data;
            unless ($bare) {
                push @output, 'start-stop' => process 'catalyst/fastcgi/start-stop' => @data;
                push @output, 'monit' => process 'catalyst/fastcgi/monit' => @data;
            }
            output( $ctx, @output );
        }
        elsif ( $mode eq 'static' ) {
            output( $ctx, 'catalyst-fastcgi.lighttpd' => process 'catalyst/fastcgi/lighttpd/static' => @data );
        }
        else {
            croak "Don't understand mode \"$mode\""
        }
    }
    elsif ( $server eq 'nginx' ) {

        if ( $mode eq 'standalone' ) {
            my @output;
            push @output, 'catalyst-fastcgi.nginx' => process 'catalyst/fastcgi/nginx' => @data;
            unless ($bare) {
                push @output, 'start-stop' => process 'catalyst/fastcgi/start-stop' => @data;
                push @output, 'monit' => process 'catalyst/fastcgi/monit' => @data;
            }
            output( $ctx, @output );
        }
        else {
            croak "Don't understand mode \"$mode\""
        }
    }
    elsif ( $server eq 'start-stop' ) {
        output( $ctx, 'catalyst-fastcgi-start-stop' => process 'catalyst/fastcgi/start-stop' => @data );
    }
    elsif ( $server eq 'monit' ) {
        output( $ctx, 'catalyst-fastcgi-monit' => process 'catalyst/fastcgi/monit' => @data );
    }
    else {
        croak "Don't understand server \"$server\""
    }

};

on 'monit' => 
    [qw/ output=s home=s monit-home=s /] => sub {
    my $ctx = shift;

    my @home;
    unless ($home[0] = $ctx->option( 'home' )) {
        @home = qw/ . my-monit /;
    }
    my $home = dir @home;
    $home = $home->absolute;
    output( $ctx, monit => process 'monit' => ( home => $home ) );
};

on 'help' => 
    undef, sub {
    my $ctx = shift;

    do_help $ctx;
};

on qr/.*/ => undef, sub {
    my $ctx = shift;

    my $path = join ' ', $ctx->path;
    print <<_END_;
Don't understand command: $path

Usage: for-example [--help] ...

    catalyst/fastcgi apache2 standalone|static|dynamic
    catalyst/fastcgi lighttpd standalone|static
    catalyst/fastcgi nginx
    catalyst/fastcgi start-stop|monit
    catalyst/mod_perl
    monit

    help

_END_
    exit -1;
};

no Getopt::Chain::Declare;

__PACKAGE__; # End of App::ForExample