File Coverage

blib/lib/Ubic/Service/Hypnotoad.pm
Criterion Covered Total %
statement 43 95 45.2
branch 7 42 16.6
condition 9 22 40.9
subroutine 10 17 58.8
pod 5 6 83.3
total 74 182 40.6


line stmt bran cond sub pod time code
1             package Ubic::Service::Hypnotoad;
2             # ABSTRACT: Ubic service module for Mojolicious Hypnotoad
3             $Ubic::Service::Hypnotoad::VERSION = '0.3005';
4 1     1   13234 use strict;
  1         2  
  1         26  
5 1     1   4 use warnings;
  1         0  
  1         24  
6              
7 1     1   380 use parent qw(Ubic::Service::Skeleton);
  1         205  
  1         3  
8              
9 1     1   18804 use Ubic::Result qw(result);
  1         2  
  1         44  
10 1     1   4 use File::Basename;
  1         2  
  1         58  
11 1     1   3 use Time::HiRes qw(time);
  1         1  
  1         5  
12 1     1   478 use Capture::Tiny qw(:all);
  1         16002  
  1         108  
13 1     1   388 use File::Spec::Functions qw(catfile file_name_is_absolute);
  1         509  
  1         639  
14              
15              
16              
17              
18             sub new {
19 2     2 0 1374 my ($class, $opt) = @_;
20              
21 2 100 50     11 my $bin = ref $opt->{bin} eq 'ARRAY' ? $opt->{bin} : [grep {length} split /\s+/, ($opt->{'bin'} // 'hypnotoad')];
  4         4  
22 2 50       4 @$bin or die "missing 'bin' parameter in new";
23 2   50     5 my $app = $opt->{'app'} // '';
24 2 50       4 length $app or die "missing 'app' parameter in new";
25 2 50       5 file_name_is_absolute($app) or die "The 'app' parameter must be an absolute path";
26 2   66     63 my $pid_file = $opt->{'pid_file'} // catfile(dirname($app), 'hypnotoad.pid');
27 2 50       4 file_name_is_absolute($pid_file) or die "The 'pid_file' parameter must be an absolute path";
28 2 50       10 length $pid_file or die "missing 'pid_file' parameter in new";
29              
30 2   50     3 my %env = %{ $opt->{'env'} // {} };
  2         9  
31              
32 2         6 my $wait_status = _calc_wait_status($opt->{wait_status});
33              
34 2         14 return bless {
35             bin => $bin,
36             app => $app,
37             env => \%env,
38             pid_file => $pid_file,
39             start_time => undef,
40             stop_time => undef,
41             cwd => $opt->{cwd},
42             wait_status => $wait_status,
43             }, $class;
44             }
45              
46             sub _calc_wait_status {
47 2     2   3 my $wait_status = shift;
48 2   100     5 my $step = $wait_status->{step} // 0.1;
49 2   100     5 my $trials = $wait_status->{trials} // 10;
50              
51 2         5 my $time_to_wait = $step * ($trials - 1) * $trials / 2 + 1;
52              
53             return {
54 2         6 step => $step,
55             trials => $trials,
56             time_to_wait => $time_to_wait,
57             };
58             }
59              
60             sub _read_pid {
61 0     0     my $self = shift;
62              
63 0           return eval {
64 0 0         open my $fh, "<", $self->{'pid_file'} or die;
65 0           my $pid = (scalar(<$fh>) =~ /(\d+)/g)[0];
66 0           close $fh;
67 0           $pid;
68             };
69             }
70              
71             sub status_impl {
72 0     0 1   my $self = shift;
73              
74 0           my $pid = $self->_read_pid;
75              
76 0 0 0       if ($self->{'start_time'} and $self->{'start_time'} + $self->{wait_status}{time_to_wait} > time) {
77 0 0         return result('broken') if ! $pid;
78             }
79 0           $self->{'start_time'} = undef;
80              
81 0 0         if (! $pid) {
82 0           $self->{'stop_time'} = undef;
83 0           return result('not running');
84             }
85              
86 0 0 0       if ($self->{'stop_time'} and $self->{'stop_time'} + $self->{wait_status}{time_to_wait} > time) {
87 0           return result('broken');
88             }
89              
90 0           my ($i, $running, $old_pid) = (0);
91 0   0       do {
92 0           $i++;
93 0           $old_pid = $pid;
94 0           $running = kill 0, $old_pid;
95 0 0         $pid = $self->_read_pid or return result('not running');
96             } until ($pid == $old_pid or $i > 5);
97              
98 0 0         $pid == $old_pid or return result('broken');
99              
100 0 0         return $running ? result('running', 'pid '.$pid) : result('not running');
101             }
102              
103             sub start_impl {
104 0     0 1   my $self = shift;
105              
106 0           local %ENV = (%ENV, %{ $self->{'env'} });
  0            
107              
108 0 0         if (defined $self->{cwd}) {
109 0 0         chdir $self->{cwd} or die "chdir to '$self->{cwd}' failed: $!";
110             }
111              
112 0           system(@{$self->{'bin'}}, $self->{'app'});
  0            
113 0           $self->{'start_time'} = time;
114 0           $self->{'stop_time'} = undef;
115              
116 0           return result('starting');
117             }
118              
119             sub stop_impl {
120 0     0 1   my $self = shift;
121              
122 0 0         if (defined $self->{cwd}) {
123 0 0         chdir $self->{cwd} or die "chdir to '$self->{cwd}' failed: $!";
124             }
125              
126 0           local %ENV = (%ENV, %{ $self->{'env'} });
  0            
127             my (undef, $stderr) = capture {
128 0     0     system(@{$self->{'bin'}}, '-s', $self->{'app'});
  0            
129 0           };
130 0 0         print $stderr if length $stderr;
131 0           $self->{'stop_time'} = time;
132 0           $self->{'start_time'} = undef;
133              
134 0           return result('stopping');
135             }
136              
137             sub reload {
138 0     0 1   my $self = shift;
139              
140 0 0         my $pid = $self->_read_pid or return 'not running';
141 0           my $ret = kill "USR2", $pid;
142 0 0         return $ret ? 'reloaded' : 'not running';
143             }
144              
145             sub timeout_options {
146 0     0 1   my $self = shift;
147              
148             return {
149 0           start => {
150             step => $self->{wait_status}{step},
151             trials => $self->{wait_status}{trials},
152             },
153             stop => {
154             step => $self->{wait_status}{step},
155             trials => $self->{wait_status}{trials},
156             }
157             };
158             }
159              
160              
161              
162              
163              
164             1;
165              
166             __END__