added code to SGN::Deploy::Apache to support early seting of
[sgn.git] / lib / SGN / Deploy / Apache.pm
blob83be9b606ecc04298ca7937df36c5ead2abe0b04
1 =head1 NAME
3 SGN::Deploy::Apache - deploy the SGN site on an Apache web server
5 =head1 SYNOPSIS
7 # in your apache conf:
9 <VirtualHost *:80>
11 ServerName sgn.localhost.localdomain
13 PerlWarn On
14 PerlTaintCheck On
16 LogLevel error
18 <Perl>
20 use lib '/crypt/rob/cxgn/curr/sgn/lib';
21 use lib '/crypt/rob/cxgn/curr/ITAG/lib';
22 use lib '/crypt/rob/cxgn/curr/Phenome/lib';
23 use lib '/crypt/rob/cxgn/curr/tomato_genome/lib';
24 use lib '/crypt/rob/cxgn/curr/Cview/lib';
25 use lib '/crypt/rob/cxgn/curr/cxgn-corelibs/lib';
26 use local::lib '/crypt/rob/cpan-lib';
28 use SGN::Deploy::Apache SGN => (
29 type => 'mod_perl',
30 vhost => 1,
31 env => {
32 SGN_CONFIG => '/etc/cxgn/SGN.conf',
36 </Perl>
38 </VirtualHost>
40 =head1 METHODS
42 =cut
44 package SGN::Deploy::Apache;
45 use strict;
46 use warnings;
47 use 5.10.0;
49 use Carp;
50 use File::Spec;
51 use File::Basename;
52 use File::Path qw/ mkpath /;
54 use Class::MOP;
56 =head2 import( $app_class, %options )
58 Status : public
59 Usage : use SGN::Deploy::Apache MyApp => ( vhost => 1, type => 'mod_perl' );
60 Returns : nothing meaningful
61 Args : app name,
62 hash-style list of arguments as:
64 vhost => boolean of whether this
65 configuration should be applied
66 to the current virtual host (if true),
67 or the root Apache server (if false),
69 type => one of the deployment types listed below
71 env => hashref of environment variables to set. This
72 must be used instead of Apache's SetEnv or
73 PerlSetEnv in order for environment variables to
74 be available early enough to be used during
75 Catalyst's setup phases.
77 Side Eff: loads the app, and configures the Apache environment to
78 properly run it
80 Configures the currently running Apache mod_perl server
81 to run this application.
83 Example :
85 In an Apache configuration file:
87 <VirtualHost *:80>
89 PerlWarn On
90 PerlTaintCheck On
92 LogLevel error
94 #the name of the virtual host we are defining
95 ServerName myapp.example.com
97 <Perl>
99 use local::lib qw( /usr/local/MyApp/local-lib );
100 use lib qw( /usr/local/MyApp/lib );
102 use MyApp;
103 MyApp->setup_apache( vhost => 1, type => 'fcgi' );
105 </Perl>
107 </VirtualHost>
109 =cut
111 sub import {
112 my $class = shift;
113 my $app = shift;
115 $class->configure_apache( $app, @_ );
117 sub _load_app {
118 my ( $class, $app ) = @_;
119 Class::MOP::load_class( $app );
121 for (qw( path_to config )) {
122 $app->can($_) or confess "$class requires $app to support the '$_' method";
127 sub configure_apache {
128 my ( $class, $app, %args ) = @_;
129 $args{type} ||= 'fcgi';
131 if( my $env = $args{env} ) {
132 $class->setup_env( $env );
135 my $setup_method = "configure_apache_$args{type}";
136 unless( $class->can($setup_method) ) {
137 croak "'$args{type}' Apache configuration type not supported by $class";
139 $class->$setup_method( $app, \%args );
142 # set the environment variables given in the hashref
143 sub setup_env {
144 my ( $class, $env_to_add ) = @_;
146 while( my ($k,$v) = each %$env_to_add ) {
147 $ENV{$k} = $v;
151 sub apache_server {
152 my ($class, $args) = @_;
153 exists $args->{vhost}
154 or confess
155 "'vhost' argument is required for apache mod_perl configuration\n";
157 require Apache2::ServerUtil;
158 require Apache2::ServerRec;
160 # add some other configuration to the web server
161 my $server = Apache2::ServerUtil->server;
162 # vhost currently being configured should be first in apache's list
163 $server = $server->next if $args->{vhost};
164 return $server;
168 =head1 SUPPORTED CONFIGURATION TYPES
170 =head2 mod_perl
172 Run the application as an Apache mod_perl handler.
174 =cut
176 sub configure_apache_mod_perl {
177 my ( $class, $app, $args ) = @_;
179 # force CGI.pm into non-mod-perl mode so that the Catalyst CGI
180 # wrapping will work.
181 # notice that we do this BEFORE loading the app
182 require CGI;
183 $CGI::MOD_PERL = 0;
185 $class->_load_app( $app );
187 my $server = $class->apache_server( $args );
189 my $app_name = $app->config->{name};
190 my $cfg = $app->config;
191 -d $cfg->{home} or confess <<EOM;
192 FATAL: Could not figure out the home dir for $app_name, it
193 guessed '$cfg->{home}', but that directory does not exist. Aborting start.
196 my $error_email = $cfg->{feedback_email} || $ENV{SERVER_ADMIN};
197 $server->add_config( $_ ) for map [ split /\n/, $_ ],
199 'ServerSignature Off',
201 # email address for apache to include in really bad error messages
202 qq|ServerAdmin $cfg->{email}|,
204 #where to write error messages
205 "ErrorLog ".$class->check_logfile( $cfg->{error_log} ),
206 "CustomLog ".$class->check_logfile( $cfg->{access_log} ).' combined',
208 # needed for CGI.pm compat
209 'PerlOptions +GlobalRequest',
211 ( 'ErrorDocument 500 "Internal server error: The server encountered an internal error or misconfiguration and was unable to complete your request.'
212 .( $error_email ? <<"" : '"' )
213 Additionally, an error report could not be automatically sent, please help us by informing us of the error at $error_email."
217 # set our application to handle most requests by default
218 "<Location />
219 SetHandler modperl
220 PerlResponseHandler $app
222 . $class->_apache_access_control_str( $cfg )
223 ."</Location>\n",
225 $class->_conf_serve_static( $app ),
226 $class->_conf_features( $app ),
232 # return configuration for serving static files with Apache
233 sub _conf_serve_static {
234 my ( $class, $app ) = @_;
236 my $cfg = $app->config;
238 # serve files directly from the static/ subdir of the site,
239 # following the symlinks therein
240 return
241 ( map {
242 my $url = "/$_";
243 my $dir = $app->path_to( $cfg->{root}, $url );
244 qq|Alias $url $dir|,
245 "<Location $url>\n"
246 ." SetHandler default-handler\n"
247 ."</Location>\n",
249 @{ $cfg->{static}->{dirs} }
251 '<Directory '.$app->path_to($cfg->{root}).qq|>\n|
252 ." Options +Indexes -ExecCGI +FollowSymLinks\n"
253 ." Order allow,deny\n"
254 ." Allow from all\n"
255 ."</Directory>\n"
260 # =head2 fcgi
262 # Run the application under Apache's mod_fcgid
264 # =cut
266 # sub configure_apache_fcgi {
267 # confess 'Unimplemented';
271 # given the context obj, return list of apache conf strings for each
272 # of the activated features
273 sub _conf_features {
274 my ($class,$app) = @_;
275 my @confs;
276 if( $app->can('enabled_features') ) {
277 for ( $app->enabled_features ) {
278 push @confs, $_->apache_conf if $_->can('apache_conf');
281 return @confs;
284 # no arguments looks at the values of $self->{production_server} and
285 # $hostconf{shared_devel_server} to generate an access control
286 # configuration
287 sub _apache_access_control_str {
288 my ($class,$cfg) = @_;
290 if ( $cfg->{production_server} ) {
291 # for production servers, allow connections from everywhere
292 <<EOT;
293 # below is the access profile for a production webserver. allow connections from anywhere.
294 Order allow,deny
295 Allow from all
297 } elsif ( $cfg->{shared_devel_server} ) {
298 # for a shared development server, allow connections from just
299 # a few places, and require passwords
300 my $auth_user_file = "/etc/cxgn/htpasswd-sgn";
301 -f $auth_user_file
302 or die "shared_devel_server enabled, but no htpasswd file ($auth_user_file) found. aborting configuration generation.";
303 <<EOT
304 # below is the access profile for a shared development server, only allow connections from a list of trusted hosts
305 # and subnets
306 AllowOverride None
307 AuthName "Development site; contact $cfg->{email} to request access"
308 AuthType Basic
309 AuthUserFile $auth_user_file
310 Require valid-user
311 Order deny,allow
312 Deny from all
313 Allow from 127.0.0.0/16
314 Allow from 132.236.157.64/26
315 Allow from 128.253.40.0/26
316 Allow from 132.236.81.0/24
317 Allow from 128.84.197.64/26
318 Satisfy Any
320 } else {
321 <<EOT
322 # below is the access profile for a personal development server: only allow connections from 127.0.*.*
323 Order deny,allow
324 Deny from all
325 Allow from 127.0.0.0/16
326 Allow from 192.168.0.0/16
327 Allow from 172.16.0.0/12
328 Allow from 10.0.0.0/8
333 # three different windows into apache internals:
334 sub _apache_debugging {
335 return <<EOC;
337 # 1.) the Apache server status plugin
338 # add a server-status location where you can monitor your
339 # you must run 'sudo a2enmod status' for this to work
340 <Location /server-status>
341 SetHandler server-status
343 Order Deny,Allow
344 Deny from all
345 Allow from 127.0.1.1
346 Allow from *.sgn.cornell.edu
347 </Location>
349 # 2.) the mod_perl status plugin
350 # add a /perl-status page where you can see mod_perl-specific status
351 # information
352 <Location /perl-status>
353 SetHandler perl-script
354 PerlHandler Apache2::Status
356 Order Deny,Allow
357 Deny from all
358 Allow from 127.0.1.1
359 Allow from *.sgn.cornell.edu
360 </Location>
361 PerlSetVar StatusOptionsAll On
363 # 3.) support for interactive debugging of the mod_perl process
364 # enable apachedb if being run in debug mode
365 # under Debian, use it by running:
366 # sudo APACHE_RUN_USER=www-data APACHE_RUN_GROUP=www-data /usr/sbin/apache2 -D PERLDB -X
367 <IfDefine PERLDB>
368 <Perl>
369 use Apache::DB ();
370 Apache::DB->init;
371 </Perl>
373 <Location />
374 PerlFixupHandler Apache::DB
375 </Location>
377 </IfDefine>
381 sub check_logfile {
382 my $context = shift;
383 my $file = File::Spec->catfile(@_);
385 return $file if -w $file;
387 my $dir = dirname($file);
389 return $file if -w $dir;
391 -d $dir
392 or do { my $r = mkpath($dir); chmod 0755, $dir}
393 or die "cannot open log file '$file', dir '$dir' does not exist and I could not create it";
395 -w $dir
396 or die "cannot open log file '$file', dir '$dir' is not writable";
398 return $file;
401 sub tee (@) {
402 print "$_\n" for @_;
403 return @_;