File Coverage

lib/Apache/SiteConfig/Deploy.pm
Criterion Covered Total %
statement 19 21 90.4
branch n/a
condition n/a
subroutine 7 7 100.0
pod n/a
total 26 28 92.8


line stmt bran cond sub pod time code
1             package Apache::SiteConfig::Deploy;
2 1     1   5 use feature ':5.10';
  1         1  
  1         85  
3 1     1   5 use warnings;
  1         2  
  1         26  
4 1     1   5 use strict;
  1         2  
  1         26  
5 1     1   6 use File::Basename qw(dirname);
  1         1  
  1         95  
6 1     1   5 use File::Spec;
  1         1  
  1         24  
7 1     1   3 use File::Path qw(mkpath rmtree);
  1         1  
  1         51  
8 1     1   382 use Apache::SiteConfig::Template;
  0            
  0            
9              
10             # TODO: support template variable for paths and meta data.
11              
12             our $Single;
13              
14             END {
15             $Single->execute_task( @ARGV ) if @ARGV;
16             }
17              
18             sub import {
19             my ($class) = @_;
20             $Single = $class->new;
21             $Single->{args} = {};
22             $Single->{tasks} = {};
23              
24             # built-in tasks
25             $Single->{tasks}->{deploy} = sub { $Single->deploy( @_ ); };
26             $Single->{tasks}->{update} = sub { $Single->update( @_ ); };
27             $Single->{tasks}->{clean} = sub { $Single->clean( @_ ); };
28              
29             # setup accessors to main::
30             no strict 'refs';
31             for my $key ( qw(su chown name domain domain_alias webroot source deploy task) ) {
32             *{ 'main::' . $key } = sub {
33             ${ $class .'::'}{ $key }->( $Single , @_ );
34             };
35             }
36              
37             # Exporter->import( @_ );
38             return 1;
39             }
40              
41             sub new { bless {} , shift; }
42              
43             sub execute_task {
44             my ($self,$task_name,@args) = @_;
45             my $task = $self->{tasks}->{ $task_name };
46             if ( $task ) {
47             $task->( $self , @args );
48             } else {
49             print "Task $task_name not found.\n";
50             }
51             }
52              
53             sub execute_command {
54             my ($self,$cmd, $abort_on_failure) = @_;
55              
56             if( $self->{args}->{su} ) {
57             $cmd = sprintf( 'sudo -u %s %s', $self->{args}->{su} , $cmd );
58             }
59              
60             say $cmd;
61             if( $abort_on_failure ) {
62             system( $cmd ) == 0 or die $!;
63             } else {
64             system( $cmd );
65             }
66             }
67              
68             sub chown {
69             my $self = shift;
70             $self->{args}->{chown} = $_[0];
71             }
72              
73             sub su {
74             my $self = shift;
75             $self->{args}->{su} = $_[0];
76             }
77              
78             sub name ($) {
79             my $self = shift;
80             $self->{args}->{name} = $_[0];
81             }
82              
83             sub domain {
84             my $self = shift;
85             $self->{args}->{domain} = $_[0];
86             }
87              
88             sub domain_alias {
89             my $self = shift;
90             $self->{args}->{domain_alias} = $_[0];
91             }
92              
93             sub source {
94             my ($self,$type,$uri) = @_;
95             $self->{args}->{source} ||= {};
96             $self->{args}->{source}->{ $type } = $uri;
97             }
98              
99             sub webroot {
100             my ($self,$path) = @_;
101             $self->{args}->{webroot} = $path;
102             }
103              
104             sub task ($&) {
105             my ($self,$name,$closure) = @_;
106             $self->{tasks}->{ $name } = $closure;
107             }
108              
109             sub preprocess_meta {
110             my $self = shift;
111             my $args = { %{ $self->{args} } }; # copy args
112             $args->{sites_dir} ||= File::Spec->join( '/var/sites' );
113             $args->{site_dir} ||= File::Spec->join( $args->{sites_dir} , $args->{name} );
114             $args->{document_root} = File::Spec->join(
115             $args->{site_dir} , $args->{webroot} );
116              
117             $args->{log_dir} ||=
118             File::Spec->join( $args->{sites_dir} , $args->{name} , 'apache2' , 'logs' );
119             # File::Spec->join( '/var/log/sites/' , $args->{name} , 'apache2' , 'logs' )
120              
121             $args->{access_log} ||= File::Spec->join( $args->{log_dir} , 'access.log' );
122             $args->{error_log} ||= File::Spec->join( $args->{log_dir} , 'error.log' );
123             return $args;
124             }
125              
126             sub prepare_paths {
127             my ($self,$args) = @_;
128             for my $path ( qw(sites_dir site_dir document_root) ) {
129             next unless $args->{ $path };
130             mkpath [ $args->{ $path } ] unless -e $args->{ $path };
131             }
132             }
133              
134             sub prepare_log_path {
135             my ($self,$args) = @_;
136             mkpath [ $args->{log_dir} ] unless -e $args->{log_dir};
137              
138             }
139              
140             sub clean {
141             my $self = shift;
142             my $args = $self->preprocess_meta;
143             say "Cleanning up $args->{site_dir}";
144             rmtree( $args->{site_dir} );
145             }
146              
147             sub update {
148             my $self = shift;
149             my $args = $self->preprocess_meta;
150              
151             if( $args->{source} ) {
152             chdir $args->{site_dir};
153              
154             if( $args->{source}->{git} ) {
155             my $branch = $args->{source}->{branch} || 'master';
156             $self->execute_command("git pull origin $branch") if $branch eq 'master';
157             }
158             elsif ( $args->{source}->{hg} ) {
159             $self->execute_command("hg pull -u");
160             }
161             }
162              
163             }
164              
165             sub deploy {
166             my ($self) = @_;
167             my $args = $self->preprocess_meta;
168             $self->prepare_paths( $args );
169              
170             SKIP_SOURCE_CLONE:
171             if( $args->{source} ) {
172              
173             if( $args->{source}->{git} ) {
174             last SKIP_SOURCE_CLONE if -e File::Spec->join( $args->{site_dir} , '.git' );
175             say "Cloning git repository from $args->{source}->{git} to $args->{site_dir}";
176              
177             $self->execute_command("git clone $args->{source}->{git} $args->{site_dir}",1);
178              
179             # if branch is specified, then check the branch out.
180             my $branch = $args->{source}->{branch};
181             $self->execute_command("git checkout -t origin/$branch",1) if $branch;
182              
183             # if tag is specified, then check the tag out.
184             my $tag = $args->{source}->{tag};
185             $self->execute_command("git checkout $tag -b $tag",1) if $tag;
186              
187             }
188             elsif( $args->{source}->{hg} ) {
189             last SKIP_SOURCE_CLONE if -e File::Spec->join( $args->{site_dir} , '.git' );
190              
191             say "Cloning hg repository from $args->{source}->{hg} to $args->{site_dir}";
192             $self->execute_command("hg clone $args->{source}->{hg} $args->{site_dir}") == 0 or die($?);
193             }
194              
195             }
196              
197             $self->prepare_log_path( $args );
198              
199             if( $args->{chown} ) {
200             say "Changing owner to $args->{site_dir}";
201             $self->execute_command( sprintf( 'chown -R %s: ' , $args->{site_dir} ) ,1);
202             }
203            
204              
205              
206             # Default template
207             my $template = Apache::SiteConfig::Template->new; # apache site config template
208             my $context = $template->build(
209             ServerName => $args->{domain},
210             ServerAlias => $args->{domain_alias},
211             DocumentRoot => $args->{document_root},
212             CustomLog => $args->{access_log} ,
213             ErrorLog => $args->{error_log}
214             );
215             my $config_content = $context->to_string;
216              
217             # get site config directory
218             my $apache_dir_debian = '/etc/apache2/sites-available';
219             if( -e $apache_dir_debian ) {
220             say "Apache Site Config Dir Found.";
221              
222             my $config_file = File::Spec->join( $apache_dir_debian , $args->{name} );
223              
224             if ( -e $config_file ) {
225             say "$config_file exists, skipped.";
226             } else {
227             say "Writing site config to $config_file.";
228             open my $fh , ">", $config_file;
229             print $fh $config_content;
230             close $fh;
231              
232             say "Enabling $args->{name}";
233             system("a2ensite $args->{name}");
234              
235             say "Reloading apache";
236             system("/etc/init.d/apache2 reload");
237             }
238             }
239             else {
240              
241             # try to find where apachectl is located.
242             my $apachectl_bin = qx(which apachectl);
243             chomp( $apachectl_bin );
244              
245             my $apache_dir = dirname(dirname( $apachectl_bin ));
246             my $apache_conf_dir = File::Spec->join( $apache_dir , 'conf' );
247              
248             if( -e $apache_conf_dir ) {
249             say "Found apache configuration directory: $apache_conf_dir";
250             # prepare site config dir
251             mkpath [ File::Spec->join( $apache_conf_dir , 'sites' ) ];
252              
253             my $httpd_conf = File::Spec->join( $apache_conf_dir , 'httpd.conf' );
254              
255             # write site config to apache conf dir
256             my $config_file = File::Spec->join( $apache_conf_dir , 'sites' , $args->{name} . '.conf' );
257             say "Writing config file: $config_file";
258             open my $fh , ">" , $config_file or die $!;
259             print $fh $config_content;
260             close $fh;
261              
262             say "Appending Include statement to $httpd_conf";
263             open my $fh2 , ">>" , $httpd_conf or die $!;
264             print $fh2 "###### Apache Site Configurations \n";
265             print $fh2 "Include conf/sites/$args->{name}.conf\n";
266             close $fh2;
267              
268             } else {
269             # apachectl not found
270             mkpath [ 'apache2' ];
271              
272             # TODO: run apache configtest here
273             # /opt/local/apache2/bin/apachectl -t -f /path/to/config file
274             my $config_file = File::Spec->join( 'apache2' , 'sites' , $args->{name} ); # apache config
275             mkpath [ File::Spec->join('apache2','sites') ];
276              
277             say "Writing site config file: $config_file";
278             open my $fh , ">", $config_file or die "Can not write config file $config_file: $!";
279             print $fh $config_content;
280             close $fh;
281             }
282              
283             }
284              
285              
286             }
287              
288              
289             1;
290             __END__
291              
292             =head1 NAME
293              
294             Apache::SiteConfig::Deploy
295              
296             =head1 SYNOPSIS
297              
298             use Apache::SiteConfig::Deploy;
299              
300             name 'projectA';
301              
302             domain 'foo.com';
303              
304             domain_alias 'foo.com';
305              
306              
307             su 'www-data';
308             chown 'www-data';
309              
310             source git => 'git@git.foo.com:projectA.git';
311              
312             source
313             git => 'git@git.foo.com:projectA.git',
314             branch => 'master';
315              
316             source hg => 'http://.........';
317              
318              
319             # relative web document path of repository
320             webroot 'webroot/';
321              
322             task deploy => sub {
323              
324             };
325              
326             task dist => sub {
327              
328             };
329              
330              
331              
332             Deploy->new(
333             name => 'projectA',
334             sites_dir => '/var/sites', # optional
335             git => 'git@foo.com:projectA.git',
336             domain => 'foo.com',
337             webroot => 'webroot/',
338             );
339              
340             =cut