File Coverage

blib/lib/Ukigumo/Agent/Manager.pm
Criterion Covered Total %
statement 101 108 93.5
branch 17 22 77.2
condition 6 7 85.7
subroutine 24 24 100.0
pod 0 5 0.0
total 148 166 89.1


line stmt bran cond sub pod time code
1             package Ukigumo::Agent::Manager;
2 14     14   383244 use strict;
  14         30  
  14         406  
3 14     14   59 use warnings;
  14         30  
  14         337  
4 14     14   64 use utf8;
  14         20  
  14         82  
5 14     14   6761 use Ukigumo::Agent::Cleaner qw/cleanup_old_branch_dir/;
  14         51  
  14         693  
6 14     14   3490 use Ukigumo::Client;
  14         603755  
  14         455  
7 14     14   11165 use Ukigumo::Client::VC::Git;
  14         112435  
  14         383  
8 14     14   11652 use Ukigumo::Client::Executor::Perl;
  14         10346  
  14         396  
9 14     14   92 use Ukigumo::Helper qw/normalize_path/;
  14         42  
  14         715  
10 14     14   68 use Ukigumo::Logger;
  14         29  
  14         268  
11 14     14   4467 use Coro;
  14         38189  
  14         1089  
12 14     14   3667 use Coro::AnyEvent;
  14         35515  
  14         472  
13 14     14   1255 use POSIX qw/SIGTERM SIGKILL/;
  14         7829  
  14         119  
14 14     14   16087 use File::Spec;
  14         30  
  14         272  
15 14     14   62 use Carp ();
  14         22  
  14         235  
16              
17 14     14   256 use Mouse;
  14         20  
  14         93  
18              
19             has 'config' => (
20             is => 'rw',
21             isa => 'HashRef',
22             lazy => 1,
23             default => sub { +{} },
24             );
25              
26             has 'work_dir' => (
27             is => 'ro',
28             isa => 'Str',
29             lazy => 1,
30             default => sub { shift->config->{work_dir} // File::Spec->tmpdir },
31             );
32              
33             has 'server_url' => (
34             is => 'ro',
35             isa => 'Str',
36             lazy => 1,
37             default => sub { shift->config->{server_url} },
38             );
39              
40             has 'timeout' => (
41             is => 'ro',
42             isa => 'Int',
43             lazy => 1,
44             default => sub { shift->config->{timeout} // 0 },
45             );
46              
47             has 'ignore_github_tags' => (
48             is => 'ro',
49             isa => 'Bool',
50             lazy => 1,
51             default => sub { shift->config->{ignore_github_tags} // 0 },
52             );
53              
54             has 'force_git_url' => (
55             is => 'ro',
56             isa => 'Bool',
57             lazy => 1,
58             default => sub { shift->config->{force_git_url} // 0 },
59             );
60              
61             has 'max_children' => (
62             is => 'ro',
63             isa => 'Int',
64             lazy => 1,
65             default => sub { shift->config->{max_children} // 1 },
66             );
67              
68             has 'cleanup_cycle' => (
69             is => 'ro',
70             isa => 'Int',
71             lazy => 1,
72             default => sub { shift->config->{cleanup_cycle} || 0 },
73             );
74              
75             has 'job_queue' => (
76             is => 'ro',
77             isa => 'ArrayRef',
78             default => sub { +[] },
79             );
80              
81             has 'children' => (
82             is => 'rw',
83             isa => 'HashRef',
84             default => sub { +{} },
85             );
86              
87             has 'logger' => (
88             is => 'ro',
89             isa => 'Ukigumo::Logger',
90             lazy => 1,
91             default => sub { Ukigumo::Logger->new },
92             );
93              
94 14     14   9276 no Mouse;
  14         25  
  14         69  
95              
96             sub count_children {
97 56     56 0 127 my $self = shift;
98 56         174 0+(keys %{$self->children});
  56         754  
99             }
100              
101             sub push_job {
102 14     14 0 92 my ($self, $job) = @_;
103 14         44 push @{$self->{job_queue}}, $job;
  14         92  
104             }
105              
106             sub pop_job {
107 9     9 0 34 my ($self, $job) = @_;
108 9         49 pop @{$self->{job_queue}};
  9         116  
109             }
110              
111             sub run_job {
112 29     29 0 85 my ($self, $args) = @_;
113 29 100       444 Carp::croak("Missing args") unless $args;
114              
115 28   100     175 my $repository = $args->{repository} || die;
116 27   100     136 my $branch = $args->{branch} || die;
117              
118 26         1060 my $vc = Ukigumo::Client::VC::Git->new(
119             branch => $branch,
120             repository => $repository,
121             );
122 26         2036 my $client = Ukigumo::Client->new(
123             workdir => $self->work_dir,
124             vc => $vc,
125             executor => Ukigumo::Client::Executor::Perl->new(),
126             server_url => $self->server_url,
127             compare_url => $args->{compare_url},
128             repository_owner => $args->{repository_owner},
129             repository_name => $args->{repository_name},
130             );
131              
132 26         22508 my $client_log_filename = $client->logfh->filename;
133              
134 26         253 my $timeout_timer;
135              
136 26         58299 my $pid = fork();
137 26 50       2101 if (!defined $pid) {
138 0         0 die "Cannot fork: $!";
139             }
140              
141 26 100       1376 if ($pid) {
142 19         2123 $self->logger->infof("Spawned $pid");
143             $self->{children}->{$pid} = +{
144             child => AE::child($pid, unblock_sub {
145 17     17   26325783 my ($pid, $status) = @_;
146              
147 17         104 undef $timeout_timer;
148              
149             # Process has terminated because it was timeout
150 17 100       230 if ($status == SIGTERM) {
151 2         56 Coro::AnyEvent::sleep 5;
152 2 50       10008392 if (kill 0, $pid) {
153             # Process is still alive
154 0         0 kill SIGTERM, $pid;
155 0         0 Coro::AnyEvent::sleep 5;
156 0 0       0 if (kill 0, $pid) {
157             # The last resort
158 0         0 kill SIGKILL, $pid;
159             }
160             }
161 2         66 $self->logger->warnf("[child] timeout");
162 2         570 eval { $client->report_timeout($client_log_filename) };
  2         56  
163             }
164              
165 17         4923 $self->logger->infof("[child exit] pid: $pid, status: $status");
166 17         4253 delete $self->{children}->{$pid};
167              
168 17 100 66     188 if ($self->count_children < $self->max_children && @{$self->job_queue} > 0) {
  17         301  
169 9         115 $self->logger->infof("[child exit] run new job");
170 9         1174 $self->run_job($self->pop_job);
171             } else {
172 8         135 $self->_take_a_break();
173             }
174 19         14603 }),
175             job => $args,
176             start => time(),
177             };
178 19         8969 my $timeout = $self->timeout;
179 19 100       1762 if ($timeout > 0) {
180             $timeout_timer = AE::timer $timeout, 0, sub {
181 2     2   1971214 kill SIGTERM, $pid;
182 3         647 };
183             }
184             } else {
185 7         772 eval { $client->run() };
  7         855  
186 7 100       5003864 $self->logger->warnf("[child] error: $@") if $@;
187              
188 7 50       1628 if (my $cleanup_cycle = $self->cleanup_cycle) {
189 0         0 my $project_dir = File::Spec->catfile($client->workdir, normalize_path($client->project));
190 0         0 cleanup_old_branch_dir($project_dir, $cleanup_cycle);
191             }
192              
193 7         293 $self->logger->infof("[child] finished to work");
194 7         5060 exit;
195             }
196             }
197              
198             sub register_job {
199 39     39 0 1092590 my ($self, $params) = @_;
200              
201 39 100       261 if ($self->count_children < $self->max_children) {
202             # run job.
203 25         128 $self->run_job($params);
204             } else {
205 14         130 $self->push_job($params);
206             }
207             }
208              
209             sub _take_a_break {
210 8     8   125 my ($self) = @_;
211 8         111 $self->logger->infof("[child exit] There is no jobs. sleep...");
212             }
213              
214             1;