File Coverage

blib/lib/Dist/Zilla/Plugin/Dpkg/PerlbrewStarman.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Dist::Zilla::Plugin::Dpkg::PerlbrewStarman;
2             {
3             $Dist::Zilla::Plugin::Dpkg::PerlbrewStarman::VERSION = '0.16';
4             }
5 1     1   26189 use Moose;
  0            
  0            
6              
7             use Moose::Util::TypeConstraints;
8              
9             extends 'Dist::Zilla::Plugin::Dpkg';
10              
11             enum 'WebServer', [qw(apache nginx all)];
12             subtype 'ApacheModule', as 'Str', where { $_ =~ /^[a-z_]+$/ };
13             subtype 'ApacheModules', as 'ArrayRef[ApacheModule]', message { 'The value provided for apache_modules does not look like a list of whitespace-separated Apache modules' };
14             coerce 'ApacheModules', from 'Str', via { [ split /\s+/ ] };
15              
16             #ABSTRACT: Generate dpkg files for your perlbrew-backed, starman-based perl app
17              
18              
19             has '+conffiles_template_default' => (
20             default => '/etc/default/{$package_name}
21             /etc/init.d/{$package_name}
22             '
23             );
24              
25             has '+control_template_default' => (
26             default => 'Source: {$package_name}
27             Section: {$package_section}
28             Priority: {$package_priority}
29             Maintainer: {$author}
30             Build-Depends: {$package_depends}
31             Standards-Version: 3.8.4
32              
33             Package: {$package_name}
34             Architecture: {$architecture}
35             Depends: adduser {$package_binary_depends}
36             Description: {$package_description}
37             '
38             );
39              
40             has '+default_template_default' => (
41             default => '# Defaults for {$package_name} initscript
42             # sourced by /etc/init.d/{$package_name}
43             # installed at /etc/default/{$package_name} by the maintainer scripts
44              
45             #
46             # This is a POSIX shell fragment
47             #
48              
49             APP="{$package_name}"
50             APPDIR="/srv/$APP"
51             APPLIB="/srv/$APP/lib"
52             APPUSER={$package_name}
53              
54             PSGIAPP="{$psgi_script}"
55             PIDFILE="/var/run/$APP.pid"
56              
57             PERLBREW_PATH="$APPDIR/perlbrew/bin"
58              
59             DAEMON_ARGS="-Ilib $PSGIAPP --daemonize --user $APPUSER --preload-app --workers {$starman_workers} --pid $PIDFILE --port {$starman_port} --host 127.0.0.1 --error-log /var/log/$APP/error.log"
60             '
61             );
62              
63             has '+init_template_default' => (
64             default => '#!/bin/sh
65             ### BEGIN INIT INFO
66             # Provides: {$package_name}
67             # Required-Start: $network $local_fs $remote_fs
68             # Required-Stop: $remote_fs
69             # Default-Start: 2 3 4 5
70             # Default-Stop: 0 1 6
71             # Short-Description: {$name}
72             # Description: {$name}
73             # <...>
74             # <...>
75             ### END INIT INFO
76              
77             # Author: {$author}
78              
79             DESC={$package_name}
80             NAME={$package_name}
81             SCRIPTNAME=/etc/init.d/$NAME
82              
83             # Read configuration variable file if it is present
84             [ -r /etc/default/$NAME ] && . /etc/default/$NAME
85              
86             PATH=$PERLBREW_PATH:$PATH
87             DAEMON=`which starman`
88              
89             # Load the VERBOSE setting and other rcS variables
90             . /lib/init/vars.sh
91              
92             # Define LSB log_* functions.
93             # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
94             . /lib/lsb/init-functions
95              
96             check_running() \{
97             [ -s $PIDFILE ] && kill -0 $(cat $PIDFILE) >/dev/null 2>&1
98             \}
99              
100             check_compile() \{
101             if ( cd $APPLIB ; find -type f -name \'*.pm\' | xargs perl -c ) ; then
102             return 1
103             else
104             return 0
105             fi
106             \}
107              
108             _start() \{
109              
110             export {$package_shell_name}_HOME=$APPDIR
111             /sbin/start-stop-daemon --start --pidfile $PIDFILE --chdir $APPDIR --exec $DAEMON -- \
112             $DAEMON_ARGS \
113             || return 2
114              
115             echo ""
116             echo "Waiting for $APP to start..."
117              
118             for i in `seq {$startup_time}` ; do
119             sleep 1
120             if check_running ; then
121             echo "$APP is now starting up"
122             return 0
123             fi
124             done
125              
126             return 1
127             \}
128              
129             start() \{
130             log_daemon_msg "Starting $APP"
131             echo ""
132              
133             if check_running; then
134             log_progress_msg "already running"
135             log_end_msg 0
136             exit 0
137             fi
138              
139             rm -f $PIDFILE 2>/dev/null
140              
141             _start
142             log_end_msg $?
143             return $?
144             \}
145              
146             stop() \{
147             log_daemon_msg "Stopping $APP"
148             echo ""
149              
150             /sbin/start-stop-daemon --stop --oknodo --pidfile $PIDFILE
151             sleep 3
152             log_end_msg $?
153             return $?
154             \}
155              
156             restart() \{
157             log_daemon_msg "Restarting $APP"
158             echo ""
159              
160             if check_compile ; then
161             log_failure_msg "Error detected; not restarting."
162             log_end_msg 1
163             exit 1
164             fi
165              
166             /sbin/start-stop-daemon --stop --oknodo --pidfile $PIDFILE
167             _start
168             log_end_msg $?
169             return $?
170             \}
171              
172              
173             # See how we were called.
174             case "$1" in
175             start)
176             start
177             ;;
178             stop)
179             stop
180             ;;
181             restart|force-reload)
182             restart
183             ;;
184             *)
185             echo $"Usage: $0 \{start|stop|restart\}"
186             exit 1
187             esac
188             exit $?
189             '
190             );
191              
192             has '+install_template_default' => (
193             default => 'config/* srv/{$package_name}/config
194             lib/* srv/{$package_name}/lib
195             root/* srv/{$package_name}/root
196             script/* srv/{$package_name}/script
197             perlbrew/* srv/{$package_name}/perlbrew
198             '
199             );
200              
201             has '+postinst_template_default' => (
202             default => '#!/bin/sh
203             # postinst script for {$package_name}
204             #
205             # see: dh_installdeb(1)
206              
207             set -e
208              
209             # summary of how this script can be called:
210             # * <postinst> `configure` <most-recently-configured-version>
211             # * <old-postinst> `abort-upgrade` <new version>
212             # * <conflictor`s-postinst> `abort-remove` `in-favour` <package>
213             # <new-version>
214             # * <postinst> `abort-remove`
215             # * <deconfigured`s-postinst> `abort-deconfigure` `in-favour`
216             # <failed-install-package> <version> `removing`
217             # <conflicting-package> <version>
218             # for details, see http://www.debian.org/doc/debian-policy/ or
219             # the debian-policy package
220              
221             PACKAGE={$package_name}
222              
223             case "$1" in
224             configure)
225              
226             # Symlink /etc/$PACKAGE to our package`s config directory
227             if [ ! -e /etc/$PACKAGE ]; then
228             ln -s /srv/$PACKAGE/config /etc/$PACKAGE
229             fi
230              
231             {$webserver_config_link}
232              
233             # Create user if it doesn`t exist.
234             if ! id $PACKAGE > /dev/null 2>&1 ; then
235             adduser --system {$uid} --home /srv/$PACKAGE --no-create-home \
236             --ingroup nogroup --disabled-password --shell /bin/bash \
237             $PACKAGE
238             fi
239              
240             # Setup the perlbrew
241             echo "export PATH=~/perlbrew/bin:$PATH" > /srv/$PACKAGE/.profile
242              
243             # Make sure this user owns the directory
244             chown -R $PACKAGE:adm /srv/$PACKAGE
245              
246             # Make the log directory
247             if [ ! -e /var/log/$PACKAGE ]; then
248             mkdir /var/log/$PACKAGE
249             chown -R $PACKAGE:adm /var/log/$PACKAGE
250             fi
251              
252             {$webserver_restart}
253             ;;
254              
255             abort-upgrade|abort-remove|abort-deconfigure)
256             ;;
257              
258             *)
259             echo "postinst called with unknown argument: $1" >&2
260             exit 1
261             ;;
262             esac
263              
264             # dh_installdeb will replace this with shell code automatically
265             # generated by other debhelper scripts.
266              
267             #DEBHELPER#
268              
269             exit 0
270             '
271             );
272              
273             has '+postrm_template_default' => (
274             default => '#!/bin/sh
275              
276             set -e
277              
278             PACKAGE={$package_name}
279              
280             case "$1" in
281             purge)
282             # Remove the config symlink
283             rm -f /etc/$PACKAGE
284              
285             # Remove the nginx config
286             if [ -h /etc/nginx/sites-available/$PACKAGE ]; then
287             rm -f /etc/nginx/sites-available/$PACKAGE
288             fi
289              
290             # Remove the apache config
291             if [ -e /etc/apache2/sites-available/$PACKAGE ]; then
292             rm -f /etc/apache2/sites-enabled/$PACKAGE
293             rm -f /etc/apache2/sites-available/$PACKAGE
294             fi
295              
296             # Remove the user
297             userdel $PACKAGE || true
298              
299             # Remove logs
300             rm -rf /var/log/$PACKAGE
301             rm -rf /var/log/apache2/$PACKAGE
302              
303             # Remove the home directory
304             rm -rf /srv/$PACKAGE
305             ;;
306              
307             remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)
308             ;;
309              
310             *)
311             echo "postrm called with unknown argument: $1" >&2
312             exit 1
313             ;;
314             esac
315              
316             #DEBHELPER#
317              
318             exit 0
319             '
320             );
321              
322             has '+rules_template_default' => (
323             default => '#!/usr/bin/make -f
324             # -*- makefile -*-
325             # Sample debian/rules that uses debhelper.
326             # This file was originally written by Joey Hess and Craig Small.
327             # As a special exception, when this file is copied by dh-make into a
328             # dh-make output file, you may use that output file without restriction.
329             # This special exception was added by Craig Small in version 0.37 of dh-make.
330              
331             # Uncomment this to turn on verbose mode.
332             export DH_VERBOSE=1
333              
334             build:
335             dh_testdir
336             dh_auto_build
337              
338             %:
339             dh $@ --without perl --without auto_configure
340             '
341             );
342              
343              
344             has 'starman_port' => (
345             is => 'ro',
346             isa => 'Str',
347             required => 1
348             );
349              
350              
351             has 'starman_workers' => (
352             is => 'ro',
353             isa => 'Str',
354             default => 5
355             );
356              
357              
358             has 'psgi_script' => (
359             is => 'ro',
360             isa => 'Str',
361             default => sub {
362             'script/'.$_[0]->package_name.'.psgi';
363             }
364             );
365              
366              
367             has 'startup_time' => (
368             is => 'ro',
369             isa => 'Str',
370             default => 30
371             );
372              
373              
374             has 'uid' => (
375             is => 'ro',
376             isa => 'Int',
377             predicate => 'has_uid'
378             );
379              
380              
381             has 'web_server' => (
382             is => 'ro',
383             isa => 'WebServer',
384             required => 1
385             );
386              
387              
388             has 'apache_modules' => (
389             is => 'ro',
390             isa => 'ApacheModules',
391             required => 0,
392             coerce => 1
393             );
394              
395             around '_generate_file' => sub {
396             my $orig = shift;
397             my $self = shift;
398             my $file = shift;
399             my $required = shift;
400             my $vars = shift;
401              
402             if($self->has_uid) {
403             $vars->{uid} = '--uid '.$self->uid;
404             }
405              
406             $vars->{starman_port} = $self->starman_port;
407             $vars->{starman_workers} = $self->starman_workers;
408             $vars->{startup_time} = $self->startup_time;
409              
410             if(($self->web_server eq 'apache') || ($self->web_server eq 'all')) {
411             $vars->{package_binary_depends} .= ', apache2';
412             $vars->{webserver_config_link} .= '# Symlink to the apache config for this environment
413             rm -f /etc/apache2/sites-available/$PACKAGE
414             ln -s /srv/$PACKAGE/config/apache/$PACKAGE.conf /etc/apache2/sites-available/$PACKAGE
415             ';
416             $vars->{webserver_restart} .= 'a2enmod proxy proxy_http rewrite ';
417             $vars->{webserver_restart} .= join ' ', @{ $self->apache_modules || [] };
418             $vars->{webserver_restart} .= '
419             a2ensite $PACKAGE
420             mkdir -p /var/log/apache2/$PACKAGE
421             if which invoke-rc.d >/dev/null 2>&1; then
422             invoke-rc.d apache2 restart
423             else
424             /etc/init.d/apache2 restart
425             fi
426             ';
427             }
428             if(($self->web_server eq 'nginx') || ($self->web_server eq 'all')) {
429             $vars->{package_binary_depends} .= ', nginx';
430             $vars->{webserver_config_link} .= '# Symlink to the nginx config for this environment
431             rm -f /etc/nginx/sites-available/$PACKAGE
432             ln -s /srv/$PACKAGE/config/nginx/$PACKAGE.conf /etc/nginx/sites-available/$PACKAGE
433             ';
434             $vars->{webserver_restart} .= 'if which invoke-rc.d >/dev/null 2>&1; then
435             invoke-rc.d nginx restart
436             else
437             /etc/init.d/nginx restart
438             fi
439             ';
440             }
441             $self->$orig($file, $required, $vars);
442             };
443              
444              
445             1;
446              
447             __END__
448             =pod
449              
450             =head1 NAME
451              
452             Dist::Zilla::Plugin::Dpkg::PerlbrewStarman - Generate dpkg files for your perlbrew-backed, starman-based perl app
453              
454             =head1 VERSION
455              
456             version 0.16
457              
458             =head1 SYNOPSIS
459              
460             A minimal directory structure for application foo:
461              
462             lib/
463             root/
464             script/foo.psgi
465             config/nginx/foo.conf
466             perlbrew/bin/starman
467              
468             A minimal configuration:
469              
470             [Dpkg::PerlbrewStarman]
471             web_server = nginx
472             starman_port = 6000
473              
474             A configuration showing optional attributes and their defaults:
475              
476             [Dpkg::PerlbrewStarman]
477             web_server = nginx
478             starman_port = 6000
479             psgi_script = script/foo.psgi
480             starman_workers = 5
481             startup_time = 30
482              
483             A configuration showing optional attributes that have no defaults:
484              
485             [Dpkg::PerlbrewStarman]
486             web_server = apache
487             starman_port = 6000
488             apache_modules = ldap ssl
489             uid = 782
490              
491             =head1 DESCRIPTION
492              
493             This L<Dist::Zilla> plugin generates Debian control files that are
494             suitable for packaging a self-contained Plack application utilizing the
495             Starman preforking PSGI HTTP server. Key features include supporting an
496             independent perl environment and the generation and installation of init
497             scripts to manage the service.
498              
499             Dist::Zilla::Plugin::Dpkg::PerlbrewStarman is an implementation of
500             L<Dist::Zilla::Plugin::Dpkg>, which itself is an abstract base class
501             more than anything. It provides the basic framework by which this
502             Dist::Zilla plugin builds the Debian control files. If the desired
503             functionality cannot be achieved by PerlbrewStarman, check there for
504             other control templates that may be overridden.
505              
506             Dist::Zilla::Plugin::Dpkg::PerlbrewStarman provides defaults for the
507             following L<Dist::Zilla::Plugin::Dpkg> stubs:
508              
509             =over 4
510              
511             =item * conffiles_template_default
512              
513             =item * control_template_default
514              
515             =item * default_template_default
516              
517             =item * init_template_default
518              
519             =item * install_template_default
520              
521             =item * postinst_template_default
522              
523             =item * postrm_template_default
524              
525             =back
526              
527             PerlbrewStarman is intended to be used to deploy applications that meet
528             the following requirements:
529              
530             =over 4
531              
532             =item * L<perlbrew> -- others have reported using PerlbrewStarman under other systems (e.g., L<Carton>)
533              
534             =item * Plack/PSGI using the L<Starman> preforking HTTP server listening on localhost
535              
536             =item * Apache and/or nginx are utilized as front-end HTTP proxies
537              
538             =item * Application may be preloaded (using Starman's --preload-app)
539              
540             =item * Application does not require root privileges
541              
542             =back
543              
544             =head2 Directory structure
545              
546             The package is installed under C</srv/$PACKAGE>. Though Debian policy
547             generally forbids packages from installing into /srv, PerlbrewStarman
548             was written for third-party distribution, not for inclusion into Debian.
549             This may change.
550              
551             By default, your application must conform to the following directory
552             structure:
553              
554             =over 4
555              
556             =item * perl environment in C<perlbrew>
557              
558             =item * application configuration in C<config>
559              
560             =item * Apache and/or nginx configuration in C<config/apache/$PACKAGE.conf> and/or C<config/nginx/$PACKAGE.conf>
561              
562             =item * PSGI and other application scripts in C<script>
563              
564             =item * application libraries in C<lib>
565              
566             =item * application templates in C<root>
567              
568             =back
569              
570             Only files located in these directories will be installed. Additional
571             files may be added to the is list by specifying a path to an alternative
572             install control file using C<install_template>. The default install
573             template looks like this:
574              
575             config/* srv/{$package_name}/config
576             lib/* srv/{$package_name}/lib
577             root/* srv/{$package_name}/root
578             script/* srv/{$package_name}/script
579             perlbrew/* srv/{$package_name}/perlbrew
580              
581             The package name is substituted for {$package_name} by Text::Template
582             via L<Dist::Zilla::Plugin::Dpkg>.
583              
584             Paths may also be removed, but note that the only path in the default
585             directory structure that is not utilized elsewhere by PerlbrewStarman
586             is C<root/*>.
587              
588             =head2 Other paths
589              
590             PerlbrewStarman creates a number of files under C</etc> in order to
591             integrate with init as well as the front-end HTTP proxy. The directory
592             C</var/log/$PACKAGE> and the link C</etc/$PACKAGE> are created as
593             normalized locations for log files and app configuration, respectively.
594             These paths should be intuitively familiar for most UNIX administrators.
595              
596             Following is a complete list of files and symlinks created:
597              
598             =over 4
599              
600             =item * /etc/init.d/$PACKAGE
601              
602             =item * /etc/default/$PACKAGE
603              
604             =item * /var/log/$PACKAGE
605              
606             =item * /etc/apache2/sites-available/$PACKAGE => /srv/$PACKAGE/config/apache/$PACKAGE.conf
607              
608             =item * /etc/nginx/sites-available/$PACKAGE => /srv/$PACKAGE/config/nginx/$PACKAGE.conf
609              
610             =item * /etc/$PACKAGE => /srv/$PACKAGE/config
611              
612             =back
613              
614             =head2 Environment
615              
616             By default, C</srv/$PACKAGE/perlbrew/bin> is prepended to the C<PATH> by
617             way of the C<PERLBREW_PATH> variable in C</etc/default/$PACKAGE>. The
618             C<starman> binary must be present in the path, else the service will
619             fail to start.
620              
621             The application runs as user $PACKAGE by way of the --user argument to
622             L<Starman>. Starman flags are specified by the C<DAEMON_ARGS> variable
623             in C</etc/default/$PACKAGE>.
624              
625             =head1 SEE ALSO
626              
627             * L<Dist::Zilla::Plugin::ChangelogFromGit::Debian>
628             * L<Dist::Zilla::Deb>
629              
630             =head1 ATTRIBUTES
631              
632             =head2 starman_port
633              
634             The port to use for starman (required).
635              
636             =head2 starman_workers
637              
638             The number of starman workers (5 by default).
639              
640             =head2 psgi_script
641              
642             Location of the psgi script started by starman. By default this is
643             C<script/$PACKAGE.psgi>.
644              
645             =head2 startup_time
646              
647             The amount of time (in seconds) that the init script will wait on startup. Some
648             applications may require more than the default amount of time (30 seconds).
649              
650             =head2 uid
651              
652             The UID of the user we're adding for the package. This is helpful for syncing
653             UIDs across multiple installations
654              
655             =head2 web_server
656              
657             Set the web server we'll be working with for this package (required).
658             Supported values are C<apache>, C<nginx>, and C<all> for both..
659              
660             =head2 apache_modules
661              
662             Set any additional Apache modules that will need to be enabled.
663              
664             =head1 AUTHOR
665              
666             Cory G Watson <gphat@cpan.org>
667              
668             =head1 COPYRIGHT AND LICENSE
669              
670             This software is copyright (c) 2013 by Infinity Interactive, Inc.
671              
672             This is free software; you can redistribute it and/or modify it under
673             the same terms as the Perl 5 programming language system itself.
674              
675             =cut
676