Merge pull request #5205 from solgenomics/topic/generic_trial_upload
[sgn.git] / lib / SGN / Deploy / Apache.pm
blobe469ce858aab074e536c07bed1334f5ae9cf9780
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 'favicon.ico',
250 'robots.txt',
251 @{ $cfg->{static}->{dirs} }
253 '<Directory '.$app->path_to($cfg->{root}).qq|>\n|
254 ." Options +Indexes -ExecCGI +FollowSymLinks\n"
255 ." Order allow,deny\n"
256 ." Allow from all\n"
257 ."</Directory>\n"
262 # =head2 fcgi
264 # Run the application under Apache's mod_fcgid
266 # =cut
268 # sub configure_apache_fcgi {
269 # confess 'Unimplemented';
273 # given the context obj, return list of apache conf strings for each
274 # of the activated features
275 sub _conf_features {
276 my ($class,$app) = @_;
277 my @confs;
278 if( $app->can('enabled_features') ) {
279 for ( $app->enabled_features ) {
280 push @confs, $_->apache_conf if $_->can('apache_conf');
283 return @confs;
286 # no arguments looks at the values of $self->{production_server} and
287 # $hostconf{shared_devel_server} to generate an access control
288 # configuration
289 sub _apache_access_control_str {
290 my ($class,$cfg) = @_;
292 if ( $cfg->{production_server} ) {
293 # for production servers, allow connections from everywhere
294 <<EOT;
295 # below is the access profile for a production webserver. allow connections from anywhere.
296 Order allow,deny
297 Allow from all
299 } elsif ( $cfg->{shared_devel_server} ) {
300 # for a shared development server, allow connections from just
301 # a few places, and require passwords
302 my $auth_user_file = "/etc/cxgn/htpasswd-sgn";
303 -f $auth_user_file
304 or die "shared_devel_server enabled, but no htpasswd file ($auth_user_file) found. aborting configuration generation.";
305 <<EOT
306 # below is the access profile for a shared development server, only allow connections from a list of trusted hosts
307 # and subnets
308 AllowOverride None
309 AuthName "Development site; contact $cfg->{email} to request access"
310 AuthType Basic
311 AuthUserFile $auth_user_file
312 Require valid-user
313 Order deny,allow
314 Deny from all
315 Allow from 127.0.0.0/16
316 Allow from 132.236.157.64/26
317 Allow from 128.253.40.0/26
318 Allow from 132.236.81.0/24
319 Allow from 128.84.197.64/26
320 Satisfy Any
322 } else {
323 <<EOT
324 # below is the access profile for a personal development server: only allow connections from 127.0.*.*
325 Order deny,allow
326 Deny from all
327 Allow from 127.0.0.0/16
328 Allow from 192.168.0.0/16
329 Allow from 172.16.0.0/12
330 Allow from 10.0.0.0/8
335 # three different windows into apache internals:
336 sub _apache_debugging {
337 return <<EOC;
339 # 1.) the Apache server status plugin
340 # add a server-status location where you can monitor your
341 # you must run 'sudo a2enmod status' for this to work
342 <Location /server-status>
343 SetHandler server-status
345 Order Deny,Allow
346 Deny from all
347 Allow from 127.0.1.1
348 Allow from *.sgn.cornell.edu
349 </Location>
351 # 2.) the mod_perl status plugin
352 # add a /perl-status page where you can see mod_perl-specific status
353 # information
354 <Location /perl-status>
355 SetHandler perl-script
356 PerlHandler Apache2::Status
358 Order Deny,Allow
359 Deny from all
360 Allow from 127.0.1.1
361 Allow from *.sgn.cornell.edu
362 </Location>
363 PerlSetVar StatusOptionsAll On
365 # 3.) support for interactive debugging of the mod_perl process
366 # enable apachedb if being run in debug mode
367 # under Debian, use it by running:
368 # sudo APACHE_RUN_USER=www-data APACHE_RUN_GROUP=www-data /usr/sbin/apache2 -D PERLDB -X
369 <IfDefine PERLDB>
370 <Perl>
371 use Apache::DB ();
372 Apache::DB->init;
373 </Perl>
375 <Location />
376 PerlFixupHandler Apache::DB
377 </Location>
379 </IfDefine>
383 sub check_logfile {
384 my $context = shift;
385 my $file = File::Spec->catfile(@_);
387 return $file if -w $file;
389 my $dir = dirname($file);
391 return $file if -w $dir;
393 -d $dir
394 or do { my $r = mkpath($dir); chmod 0755, $dir}
395 or die "cannot open log file '$file', dir '$dir' does not exist and I could not create it";
397 -w $dir
398 or die "cannot open log file '$file', dir '$dir' is not writable";
400 return $file;
403 sub tee (@) {
404 print "$_\n" for @_;
405 return @_;