File Coverage

blib/lib/App/ForExample.pm
Criterion Covered Total %
statement 19 53 35.8
branch 0 18 0.0
condition 0 9 0.0
subroutine 7 9 77.7
pod n/a
total 26 89 29.2


line stmt bran cond sub pod time code
1             package App::ForExample;
2              
3 4     4   590278 use warnings;
  4         38  
  4         155  
4 4     4   23 use strict;
  4         9  
  4         341  
5              
6             =head1 NAME
7              
8             App::ForExample - A guide through Catalyst, Apache, lighttpd, nginx, monit, ..., configuration hell
9              
10             =head1 VERSION
11              
12             Version 0.024
13              
14             =cut
15              
16             our $VERSION = '0.024';
17              
18             =head1 SYNOPSIS
19              
20             # To output a FastCGI (ExternalServer)/Apache configuration (with monit stub and start-stop script), run:
21             for-example catalyst/fastcgi apache2 standalone --class My::App --hostname example.com --output my-app
22              
23             # The above command would have created the following:
24              
25             my-app.apache2 The Apache2 virtual host configuration (hosted at (www.)example.com)
26             my-app.start-stop The start/stop script to launch the FastCGI process
27             my-app.monit A monit stub used for monitoring the FastCGI process
28              
29             # This will generate a basic, stripped-down monit configuration (monitrc) suitable for a non-root user:
30             for-example monit --home $HOME/monit --output $HOME/monit/monitrc
31              
32             # A mod_perl configuration for Catalyst:
33             for-example catalyst/mod_perl --class Project::Xyzzy --hostname xyzzy.com --home Project-Xyzzy
34              
35             =head1 DESCRIPTION
36              
37             App::ForExample is a command-line tool for generating sample configurations. It is not designed to do configuration
38             management, but rather as a guide to get you 80% of the way there
39              
40             Besides the usual Apache, lighttpd, nginx, and FastCGI configurations, App::ForExample can create a FastCGI start-stop script and a
41             monit configuration for monitoring those processes
42              
43             =head1 USAGE
44              
45             Usage: for-example ACTION
46              
47             Where ACTION can be
48              
49             (Note: Every option below is, well, optional. If not specified, a fun default will be chosen/guessed for you)
50              
51             catalyst/fastcgi ...
52              
53             Generate a Catalyst FastCGI configuration (for monit, start-stop, or the specified http daemon and fastcgi method)
54              
55             --class The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
56             --home The path to your Catalyst home directory, default: . (The current directory)
57             --log-home The directory to log into, default: <home>/log (Below the directory given by --home)
58             --base The base for your application, default: / (At the root)
59             --hostname The hostname from which your application is served (e.g. example.com)
60              
61             --bare Do not output anything BUT the configuration (no monit, no start-stop)
62             --output - Print output to stdout
63             --output <path> Write output to <path> (which can be either a directory or file)
64             This will split output appropiately (e.g. <file>.apache2, <file>.start-stop, <file>.monit)
65              
66             --fastcgi-script The <path> to the Catalyst fastcgi script (e.g. script/xyzzy_fastcgi.pl)
67             --fastcgi-socket <path> Have fastcgi use <path> for the file socket
68             --fastcgi-socket <host:port> Have fastcgi use <host:port> for the socket
69             --fastcgi-pid-file <path> Store the pid for the process in <path>
70              
71             apache2 standalone Apache2 with standalone FastCGI (mod_fastcgi)
72             apache2 static Apache2 with static FastCGI (mod_fastcgi)
73             apache2 dynamic Apache2 with dynamic FastCGI (mod_fastcgi)
74              
75             lighttpd standalone lighttpd with dynamic FastCGI
76             lighttpd static lighttpd with static FastCGI
77              
78             nginx nginx with standalone FastCGI (the only kind supported)
79              
80             monit A monit configuration for a standalone FastCGI setup
81             start-stop A start-stop script for a standalone FastCGI setup
82            
83             catalyst/mod_perl
84              
85             Generate a mod_perl2 (for Apache2) Catalyst configuration
86              
87             --class The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
88             --home The path to your Catalyst home directory, default: . (The current directory)
89             --log-home The directory to log into, default: <home>/log (Below the directory given by --home)
90             --base The base for your application, default: / (At the root)
91             --hostname The hostname from which your application is served (e.g. example.com)
92              
93             monit
94              
95             Generate a basic, stripped-down monit configuration suitable for a non-root user
96              
97             --home The directory designated monit home (containing the pid file, log, rc, ...)
98              
99             =head1 TUTORIAL
100              
101             =head2 Apache2 with FastCGI on Ubuntu
102              
103             Install apache2, mod_fastcgi, and L<FCGI>
104              
105             sudo apt-get install apache2 libapache2-mod-fastcgi
106              
107             cpan -i FCGI
108              
109             Create the Catalyst application C<My::App>
110              
111             catalyst.pl My::App
112              
113             Use L<App::ForExample> to generate the configuration
114              
115             cd My-App
116             for-example catalyst/fastcgi apache2 standalone --class My::App --hostname my-app.localhost --output my-app
117              
118             Make the log directory
119              
120             mkdir log
121              
122             Install the apache2 configuration
123              
124             sudo cp my-app.apache2 /etc/apache2/sites-enabled
125              
126             Enable the fastcgi start-stop script (with execute permissions)
127              
128             chmod +x my-app.start-stop
129              
130             Add a C<my-app.localhost> entry to C</etc/hosts>
131              
132             127.0.0.1 my-app.localhost
133            
134             Start your application
135              
136             ./my-app.start-stop start
137             sudo /etc/init.d/apache restart
138              
139             Visit your application at L<http://my-app.localhost>
140              
141             =head1 INSTALL
142              
143             You can install L<App::ForExample> by using L<CPAN>:
144              
145             cpan -i App::ForExample
146              
147             If that doesn't work properly, you can find help at:
148              
149             http://sial.org/howto/perl/life-with-cpan/
150             http://sial.org/howto/perl/life-with-cpan/macosx/ # Help on Mac OS X
151             http://sial.org/howto/perl/life-with-cpan/non-root/ # Help with a non-root account
152              
153             =head1 CONTRIBUTE
154              
155             You can contribute or fork this project via GitHub:
156              
157             L<http://github.com/robertkrimen/App-ForExample/tree/master>
158              
159             git clone git://github.com/robertkrimen/App-ForExample.git
160              
161             =cut
162              
163 4     4   2417 use App::ForExample::Catalog;
  4         11  
  4         108  
164              
165 4     4   6691 use Template;
  4         125177  
  4         135  
166 4     4   46 use Carp;
  4         8  
  4         285  
167 4     4   4064 use Path::Class;
  4         198083  
  4         2376  
168              
169             my $catalog = App::ForExample::Catalog->catalog;
170             my $tt = Template->new({ BLOCKS => $catalog->{common} });
171              
172             sub process ($@) {
173 0     0     my $given = shift;
174              
175 0           my ($template);
176 0 0         if ( ref $given eq 'SCALAR' ) {
177 0           $template = $given;
178             }
179             else {
180 0 0         $template = $catalog->{$given} or croak "Template \"$given\" does not exist in the catalog";
181             }
182              
183 0           my $output;
184 0 0         $tt->process( $template => { @_ }, \$output ) or croak "Error processing template \"$given\": ", $tt->error;
185 0           return $output;
186             }
187              
188             sub output ($@) {
189 0     0     my $ctx = shift;
190              
191 0           my $to = $ctx->option( 'output' );
192 0 0 0       $to = '-' unless defined $to && length $to;
193              
194 0 0         if ( $to eq '-' ) {
195 0           my $ii = 0;
196 0           while ( @_ ) {
197 0           shift;
198 0 0         print "---\n" if $ii++ > 0;
199 0           print shift;
200             }
201             }
202             else {
203 0           my $name_hint = shift;
204              
205 0 0         if ( $name_hint =~ m/^(.*)\.(.*)$/ ) { # E.g. catalyst-fastcgi.apache or .lighttpd
206 0           $name_hint = $1;
207 0           unshift @_, $2;
208             }
209              
210 0 0 0       if ( ! -f $to && ( $to =~ m/\/$/ || -d _ ) ) {
      0        
211 0           $to = file( $to, $name_hint );
212             }
213             else {
214 0           $to = file $to;
215             }
216              
217 0           my $parent = $to->parent;
218 0 0         $parent->mkpath unless -d $parent;
219              
220 0           while ( @_ ) {
221 0           my $name = shift;
222 0           my $content = shift;
223              
224 0           my $file = file( join '.', $to, $name );
225 0           $file->openw->print( $content );
226 0           print "Made $file\n";
227             }
228             }
229             }
230              
231 4     4   7872 use Getopt::Chain::Declare;
  0            
  0            
232              
233             sub package2name ($) {
234             my $package = shift;
235             my $name = $package;
236             $name =~ s/::/-/g;
237             $name = lc $name;
238             my $name_underscore = $name;
239             $name_underscore =~ s/-/_/g;
240             return ( $name, $name_underscore );
241             }
242              
243             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/;
244             sub parse_catalyst ($) {
245             my $ctx = shift;
246              
247             # Catalyst package
248             my $package = $ctx->option( 'package' ) || 'Project::Xyzzy';
249             my ($package_name, $name_underscore) = package2name $package;
250              
251             # Catalyst name
252             my $name = $ctx->option( 'name' );
253             $name = $package_name unless defined $name;
254              
255             # Catalyst home
256             my $home = $ctx->option( 'home' ) || "./";
257             $home = dir( $home )->absolute;
258              
259             my $log_home = $ctx->option( 'log_home' ) || $home->subdir( 'log' );
260             $log_home = dir( $log_home )->absolute;
261              
262             # Catalyst application base
263             my $base = $ctx->option( 'base' ) || '/';
264             $base =~ s/^\/+//;
265             my $alias_base = $base eq '' ? '/' : "/$base/";
266              
267             # Hostname
268             my $hostname = $ctx->option( 'hostname' ) || "$name.example.com";
269              
270             my $fastcgi_script = $ctx->option( 'fastcgi-script' );
271             $fastcgi_script = join '/', $home, 'script', "${name_underscore}_fastcgi.pl" unless defined $fastcgi_script;
272             my $fastcgi_script_basename = file( $fastcgi_script )->basename;
273             my $fastcgi_socket = $ctx->option( 'fastcgi-socket' );
274             $fastcgi_socket = "/tmp/$name.socket" unless defined $fastcgi_socket;
275             my $fastcgi_host_port;
276             if ( $fastcgi_socket =~ m/^(.+):(\d+)$/ ) {
277             $fastcgi_host_port = [ $1, $2 ];
278             }
279             my $fastcgi_socket_path = $ctx->option( 'fastcgi-socket-path' );
280             $fastcgi_socket_path = "/tmp/$name.fcgi" unless defined $fastcgi_socket_path;
281             my $fastcgi_pid_file = $ctx->option( 'fastcgi-pid-file' );
282             $fastcgi_pid_file = "$name-fastcgi.pid" unless $fastcgi_pid_file;
283             $fastcgi_pid_file = join '/', $home, $fastcgi_pid_file unless $fastcgi_pid_file =~ m/^\//;
284              
285             my @data;
286             push @data, package => $package,
287             name => $name,
288             name_underscore => $name_underscore,
289             home => $home,
290             log_home => $log_home,
291             base => $base,
292             alias_base => $alias_base,
293             hostname => $hostname,
294             fastcgi_script => $fastcgi_script,
295             fastcgi_script_basename => $fastcgi_script_basename,
296             fastcgi_socket => $fastcgi_socket,
297             fastcgi_host_port => $fastcgi_host_port,
298             fastcgi_socket_path => $fastcgi_socket_path,
299             fastcgi_pid_file => $fastcgi_pid_file,
300             ;
301             return { @data };
302             }
303              
304             sub do_help ($) {
305             my $ctx = shift;
306              
307             print <<_END_;
308             Usage: for-example ACTION
309              
310             Where ACTION can be
311              
312             (Note: Every option below is, well, optional. If not specified, a fun default will be chosen/guessed for you)
313              
314             catalyst/fastcgi ...
315              
316             Generate a Catalyst FastCGI configuration (for monit, start-stop, or the specified http daemon and fastcgi method)
317              
318             --class The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
319             --home The path to your Catalyst home directory, default: . (The current directory)
320             --log-home The directory to log into, default: <home>/log (Below the directory given by --home)
321             --base The base for your application, default: / (At the root)
322             --hostname The hostname from which your application is served (e.g. example.com)
323              
324             --bare Do not output anything BUT the configuration (no monit, no start-stop)
325             --output - Print output to stdout
326             --output <path> Write output to <path> (which can be either a directory or file)
327             This will split output appropiately (e.g. <file>.apache2, <file>.start-stop, <file>.monit)
328              
329             --fastcgi-script The <path> to the Catalyst fastcgi script (e.g. script/xyzzy_fastcgi.pl)
330             --fastcgi-socket <path> Have fastcgi use <path> for the file socket
331             --fastcgi-socket <host:port> Have fastcgi use <host:port> for the socket
332             --fastcgi-pid-file <path> Store the pid for the process in <path>
333              
334             apache2 standalone Apache2 with standalone FastCGI (mod_fastcgi)
335             apache2 static Apache2 with static FastCGI (mod_fastcgi)
336             apache2 dynamic Apache2 with dynamic FastCGI (mod_fastcgi)
337              
338             lighttpd standalone lighttpd with dynamic FastCGI
339             lighttpd static lighttpd with static FastCGI
340              
341             nginx nginx with standalone FastCGI (the only kind supported)
342              
343             monit A monit configuration for a standalone FastCGI setup
344             start-stop A start-stop script for a standalone FastCGI setup
345            
346             catalyst/mod_perl
347              
348             Generate a mod_perl2 (for Apache2) Catalyst configuration
349              
350             --class The Catalyst class for your application (e.g. Project::Xyzzy or My::App)
351             --home The path to your Catalyst home directory, default: . (The current directory)
352             --log-home The directory to log into, default: <home>/log (Below the directory given by --home)
353             --base The base for your application, default: / (At the root)
354             --hostname The hostname from which your application is served (e.g. example.com)
355              
356             monit
357              
358             Generate a basic, stripped-down monit configuration suitable for a non-root user
359              
360             --home The directory designated monit home (containing the pid file, log, rc, ...)
361              
362             For example:
363              
364             for-example catalyst/fastcgi apache2 standalone --class My::App --hostname example.com
365             for-example monit --home \$HOME/my-monit
366             for-example catalyst/mod_perl --class Project::Xyzzy --hostname xyzzy.com --home Project-Xyzzy
367              
368             _END_
369             }
370              
371             start [qw/ help|h|? /], sub {
372             my $ctx = shift;
373              
374             if ( $ctx->option( 'help' ) || $ctx->last ) {
375             do_help $ctx;
376             exit 0;
377             }
378             };
379              
380             rewrite qr#catalyst/(?:mod_perl[12]|modperl[12]?)# => 'catalyst/mod_perl';
381              
382             on 'catalyst/mod_perl *' =>
383             [ qw/ output=s /, @parse_catalyst ] => sub {
384             my $ctx = shift;
385            
386             my ($server);
387             for ( @_ ) {
388             m/(apache2?)/ and ($server) = ($1) or
389              
390             croak "Don't understand argument $_ (@_)";
391             }
392             ($server) = qw/apache2/;
393              
394             my @data;
395             my $catalyst_data = parse_catalyst $ctx;
396             push @data, %$catalyst_data;
397              
398             if ( $server =~ m/^apache2?$/ ) {
399             output( $ctx, 'catalyst-mod_perl' => process 'catalyst/mod_perl/apache2' => @data );
400             }
401             else {
402             croak "Don't understand server \"$server\""
403             }
404              
405             };
406              
407             on 'catalyst/fastcgi *' =>
408             [ @parse_catalyst, qw/ bare output=s /] => sub {
409             my $ctx = shift;
410            
411             my ($server, $server_module, $mode);
412             for ( @_ ) {
413             m/(apache2?)(?:=(?:mod_)?(fastcgi|fcgid))?/ and ($server, $server_module) = ($1, $2) or
414             m/lighttpd/ and $server = 'lighttpd' or
415             m/nginx/ and $server = 'nginx' or
416             m/(monit|start-stop)/ and $server = $1 or # Not really a server, but...
417              
418             m/standalone/ and $mode = 'standalone' or
419             m/static/ and $mode = 'static' or
420             m/dynamic/ and $mode = 'dynamic' or
421              
422             croak "Don't understand argument $_ (@_)";
423             }
424              
425             ($server, $server_module) = qw/apache2 fastcgi/ unless $server;
426             ($mode) = qw/standalone/ unless $mode;
427              
428             my @data;
429              
430             my $bare = $ctx->option( 'bare' );
431              
432             my $catalyst_data = parse_catalyst $ctx;
433             push @data, %$catalyst_data;
434             my $name = $catalyst_data->{name};
435              
436             if ( $server =~ m/^apache2?$/ ) {
437              
438             if ( $mode eq 'standalone' ) {
439             # TODO Error in Catalyst::Engine::FastCGI dox?
440             my @output;
441             push @output, 'catalyst-fastcgi.apache2' => process 'catalyst/fastcgi/apache2/standalone' => @data;
442             unless ($bare) {
443             push @output, 'start-stop' => process 'catalyst/fastcgi/start-stop' => @data;
444             push @output, 'monit' => process 'catalyst/fastcgi/monit' => @data;
445             }
446             output( $ctx, @output );
447             }
448             elsif ( $mode eq 'dynamic' ) {
449             output( $ctx, 'catalyst-fastcgi.apache2' => process 'catalyst/fastcgi/apache2/dynamic' => @data );
450             }
451             elsif ( $mode eq 'static' ) {
452             output( $ctx, 'catalyst-fastcgi.apache2' => process 'catalyst/fastcgi/apache2/static' => @data );
453             }
454             else {
455             croak "Don't understand mode \"$mode\""
456             }
457             }
458             elsif ( $server eq 'lighttpd' ) {
459              
460             if ( $mode eq 'standalone' ) {
461             my @output;
462             push @output, 'catalyst-fastcgi.lighttpd' => process 'catalyst/fastcgi/lighttpd/standalone' => @data;
463             unless ($bare) {
464             push @output, 'start-stop' => process 'catalyst/fastcgi/start-stop' => @data;
465             push @output, 'monit' => process 'catalyst/fastcgi/monit' => @data;
466             }
467             output( $ctx, @output );
468             }
469             elsif ( $mode eq 'static' ) {
470             output( $ctx, 'catalyst-fastcgi.lighttpd' => process 'catalyst/fastcgi/lighttpd/static' => @data );
471             }
472             else {
473             croak "Don't understand mode \"$mode\""
474             }
475             }
476             elsif ( $server eq 'nginx' ) {
477              
478             if ( $mode eq 'standalone' ) {
479             my @output;
480             push @output, 'catalyst-fastcgi.nginx' => process 'catalyst/fastcgi/nginx' => @data;
481             unless ($bare) {
482             push @output, 'start-stop' => process 'catalyst/fastcgi/start-stop' => @data;
483             push @output, 'monit' => process 'catalyst/fastcgi/monit' => @data;
484             }
485             output( $ctx, @output );
486             }
487             else {
488             croak "Don't understand mode \"$mode\""
489             }
490             }
491             elsif ( $server eq 'start-stop' ) {
492             output( $ctx, 'catalyst-fastcgi-start-stop' => process 'catalyst/fastcgi/start-stop' => @data );
493             }
494             elsif ( $server eq 'monit' ) {
495             output( $ctx, 'catalyst-fastcgi-monit' => process 'catalyst/fastcgi/monit' => @data );
496             }
497             else {
498             croak "Don't understand server \"$server\""
499             }
500              
501             };
502              
503             on 'monit' =>
504             [qw/ output=s home=s monit-home=s /] => sub {
505             my $ctx = shift;
506              
507             my @home;
508             unless ($home[0] = $ctx->option( 'home' )) {
509             @home = qw/ . my-monit /;
510             }
511             my $home = dir @home;
512             $home = $home->absolute;
513             output( $ctx, monit => process 'monit' => ( home => $home ) );
514             };
515              
516             on 'help' =>
517             undef, sub {
518             my $ctx = shift;
519              
520             do_help $ctx;
521             };
522              
523             on qr/.*/ => undef, sub {
524             my $ctx = shift;
525              
526             my $path = join ' ', $ctx->path;
527             print <<_END_;
528             Don't understand command: $path
529              
530             Usage: for-example [--help] ...
531              
532             catalyst/fastcgi apache2 standalone|static|dynamic
533             catalyst/fastcgi lighttpd standalone|static
534             catalyst/fastcgi nginx
535             catalyst/fastcgi start-stop|monit
536             catalyst/mod_perl
537             monit
538              
539             help
540              
541             _END_
542             exit -1;
543             };
544              
545             no Getopt::Chain::Declare;
546              
547             =head1 SEE ALSO
548              
549             L<http://dev.catalystframework.org/wiki/deployment>
550              
551             L<Catalyst::Engine::Apache>
552              
553             L<Catalyst::Engine::FastCGI>
554              
555             =head1 ACKNOWLEDGEMENTS
556              
557             All the people that have put effort into the Catalyst documentation, including the pod, advent, and wiki
558              
559             Dan Dascalescu, Jay Shirley, Tomas Doran, Daniel Austin, Jason Felds, Moritz Onken, and Brian Friday, who all put effort into the deployment wiki, which
560             formed the basis for many parts of this tool
561              
562             =head1 AUTHOR
563              
564             Robert Krimen, C<< <rkrimen at cpan.org> >>
565              
566             =head1 BUGS
567              
568             Please report any bugs or feature requests to C<bug-app-forexample at rt.cpan.org>, or through
569             the web interface at L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=App-ForExample>. I will be notified, and then you'll
570             automatically be notified of progress on your bug as I make changes.
571              
572              
573              
574              
575             =head1 SUPPORT
576              
577             You can find documentation for this module with the perldoc command.
578              
579             perldoc App::ForExample
580              
581              
582             You can also look for information at:
583              
584             =over 4
585              
586             =item * RT: CPAN's request tracker
587              
588             L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=App-ForExample>
589              
590             =item * AnnoCPAN: Annotated CPAN documentation
591              
592             L<http://annocpan.org/dist/App-ForExample>
593              
594             =item * CPAN Ratings
595              
596             L<http://cpanratings.perl.org/d/App-ForExample>
597              
598             =item * Search CPAN
599              
600             L<http://search.cpan.org/dist/App-ForExample/>
601              
602             =back
603              
604              
605             =head1 ACKNOWLEDGEMENTS
606              
607              
608             =head1 COPYRIGHT & LICENSE
609              
610             Copyright 2009 Robert Krimen, all rights reserved.
611              
612             This program is free software; you can redistribute it and/or modify it
613             under the same terms as Perl itself.
614              
615              
616             =cut
617              
618             __PACKAGE__; # End of App::ForExample