File Coverage

blib/lib/DR/Tarantool/StartTest.pm
Criterion Covered Total %
statement 95 138 68.8
branch 18 54 33.3
condition 4 10 40.0
subroutine 18 26 69.2
pod 8 11 72.7
total 143 239 59.8


line stmt bran cond sub pod time code
1 10     10   26346 use utf8;
  10         23  
  10         80  
2 10     10   265 use strict;
  10         19  
  10         1422  
3 10     10   50 use warnings;
  10         19  
  10         364  
4              
5             package DR::Tarantool::StartTest;
6 10     10   48 use Carp;
  10         17  
  10         741  
7 10     10   14285 use File::Temp qw(tempfile tempdir);
  10         246966  
  10         910  
8 10     10   92 use File::Path 'rmtree';
  10         167  
  10         467  
9 10     10   2861 use File::Spec::Functions qw(catfile rel2abs);
  10         2318  
  10         607  
10 10     10   57 use Cwd;
  10         45  
  10         781  
11 10     10   12412 use IO::Socket::INET;
  10         139582  
  10         97  
12 10     10   19576 use POSIX ();
  10         78187  
  10         29718  
13              
14             =head1 NAME
15              
16             DR::Tarantool::StartTest - finds and starts Tarantool on free port.
17              
18             =head1 SYNOPSIS
19              
20             my $t = run DR::Tarantool::StartTest ( cfg => $file_spaces_cfg );
21              
22             =head1 DESCRIPTION
23              
24             The module tries to find and then to start B<tarantool_box>.
25              
26             The module is used inside tests.
27              
28              
29             =head1 METHODS
30              
31             =head2 run
32              
33             Constructor. Receives the following arguments:
34              
35             =over
36              
37             =item cfg
38              
39             path to tarantool.cfg
40              
41             =back
42              
43             =cut
44              
45              
46             sub run {
47 9     9 1 13663 my ($module, %opts) = @_;
48              
49 9 50       59 my $cfg_file = delete $opts{cfg} or croak "config file not defined";
50 9 50       179 croak "File not found" unless -r $cfg_file;
51 9 50   9   332 open my $fh, '<:encoding(UTF-8)', $cfg_file or die "$@\n";
  9         90  
  9         17  
  9         73  
52 9         17643 local $/;
53 9         321 my $cfg = <$fh>;
54              
55 9         558 my %self = (
56             admin_port => $module->_find_free_port,
57             primary_port => $module->_find_free_port,
58             secondary_port => $module->_find_free_port,
59             cfg_data => $cfg,
60             master => $$,
61             cwd => getcwd,
62             add_opts => \%opts,
63             );
64              
65 9 100       65 $opts{script_dir} = rel2abs $opts{script_dir} if $opts{script_dir};
66              
67 9         148 my $self = bless \%self => $module;
68 9         49 $self->_start_tarantool;
69 9         1229 $self;
70             }
71              
72              
73             =head2 started
74              
75             Return true if Tarantool is found and started
76              
77             =cut
78              
79             sub started {
80 9     9 1 681 my ($self) = @_;
81 9         69 return $self->{started};
82             }
83              
84              
85             =head2 log
86              
87             Return Tarantool logs
88              
89             =cut
90              
91             sub log {
92 9     9 1 152 my ($self) = @_;
93 9 50 33     510 return '' unless $self->{log} and -r $self->{log};
94 9         926 open my $fh, '<encoding(UTF-8)', $self->{log};
95 9         1334 local $/;
96 9         438 my $l = <$fh>;
97 9         716 return $l;
98             }
99              
100             sub _start_tarantool {
101 9     9   22 my ($self) = @_;
102 9 50       43 if ($ENV{TARANTOOL_TEMPDIR}) {
103 0         0 $self->{temp} = $ENV{TARANTOOL_TEMPDIR};
104 0         0 $self->{dont_unlink_temp} = 1;
105 0 0       0 rmtree $self->{temp} if -d $self->{temp};
106 0         0 mkdir $self->{temp};
107             } else {
108 9         59 $self->{temp} = tempdir;
109             }
110 9         5100 $self->{cfg} = catfile $self->{temp}, 'tarantool.cfg';
111 9         94 $self->{log} = catfile $self->{temp}, 'tarantool.log';
112 9         59 $self->{pid} = catfile $self->{temp}, 'tarantool.pid';
113 9         61 $self->{core} = catfile $self->{temp}, 'core';
114              
115              
116              
117 9         31 $self->{config_body} = $self->{cfg_data};
118 9         55 $self->{config_body} .= "\n\n";
119 9         29 $self->{config_body} .= "slab_alloc_arena = 1.1\n";
120 9         58 $self->{config_body} .= sprintf "pid_file = %s\n", $self->{pid};
121 9   50     87 $self->{box} = $ENV{TARANTOOL_BOX} || 'tarantool_box';
122              
123             $self->{config_body} .= sprintf "%s = %s\n", $_, $self->{$_}
124 9         103 for (qw(admin_port primary_port secondary_port));
125              
126 9         47 $self->{config_body} .= sprintf qq{logger = "cat >> %s"\n}, $self->{log};
127              
128 9         19 for (keys %{ $self->{add_opts} }) {
  9         42  
129 4         11 my $v = $self->{add_opts}{ $_ };
130              
131 4 100       26 if ($v =~ /^\d+$/) {
132 1         5 $self->{config_body} .= sprintf qq{%s = %s\n}, $_, $v;
133             } else {
134 3         18 $self->{config_body} .= sprintf qq{%s = "%s"\n}, $_, $v;
135             }
136             }
137              
138 9 50       699 return unless open my $fh, '>:encoding(UTF-8)', $self->{cfg};
139 9         1492 print $fh $self->{config_body};
140 9         346 close $fh;
141              
142 9         159 chdir $self->{temp};
143              
144 9         67250 system "$self->{box} -c $self->{cfg} --check-config >> $self->{log} 2>&1";
145 9 50       517 goto EXIT if $?;
146              
147 0         0 system "$self->{box} -c $self->{cfg} --init-storage ".
148             ">> $self->{log} 2>&1";
149 0 0       0 goto EXIT if $?;
150 0         0 $self->_restart;
151 9         955 EXIT:
152             chdir $self->{cwd};
153              
154             }
155              
156             sub _restart {
157 0     0   0 my ($self) = @_;
158              
159 0 0       0 unless ($self->{child} = fork) {
160 0         0 chdir $self->{temp};
161 0 0       0 die "Can't fork: $!" unless defined $self->{child};
162 0         0 POSIX::setsid();
163 0         0 exec "ulimit -c unlimited; ".
164             "exec $self->{box} -c $self->{cfg} >> $self->{log} 2>&1";
165 0         0 die "Can't start $self->{box}: $!\n";
166             }
167              
168 0         0 $self->{started} = 1;
169              
170              
171             # wait for starting Tarantool
172 0         0 for (my $i = 0; $i < 100; $i++) {
173 0 0       0 last if IO::Socket::INET->new(
174             PeerAddr => '127.0.0.1', PeerPort => $self->primary_port
175             );
176              
177 0         0 sleep 0.01;
178             }
179              
180 0         0 for (my $i = 0; $i < 100; $i++) {
181 0 0       0 last if $self->log =~ /entering event loop/;
182 0         0 sleep 0.01;
183             }
184              
185 0 0       0 sleep 1 unless $self->log =~ /entering event loop/;
186             }
187              
188             sub restart {
189 0     0 0 0 my ($self) = @_;
190 0         0 $self->kill('KILL');
191 0         0 $self->_restart;
192             }
193              
194             =head2 primary_port
195              
196             Return Tarantool primary port
197              
198             =cut
199              
200 0     0 1 0 sub primary_port { return $_[0]->{primary_port} }
201              
202              
203             =head2 admin_port
204              
205             Return Tarantool admin port
206              
207             =cut
208              
209 0     0 1 0 sub admin_port { return $_[0]->{admin_port} }
210              
211              
212             =head2 tarantool_pid
213              
214             Return B<PID>
215              
216             =cut
217              
218 0     0 1 0 sub tarantool_pid { return $_[0]->{child} }
219              
220              
221             =head2 kill
222              
223             Kills Tarantool
224              
225             =cut
226              
227             sub kill :method {
228 9     9 1 36 my ($self, $signame) = @_;
229              
230 9   50     140 $signame ||= 'TERM';
231 9 50       67 if ($self->{child}) {
232 0         0 kill $signame => $self->{child};
233 0         0 waitpid $self->{child}, 0;
234 0         0 delete $self->{child};
235             }
236 9         79 $self->{started} = 0;
237             }
238              
239              
240             =head2 is_dead
241              
242             Return true if child Tarantool process is dead.
243              
244             =cut
245              
246             sub is_dead {
247 0     0 1 0 my ($self) = @_;
248 0 0       0 return 1 unless $self->{child};
249 0 0       0 return 0 if 0 < kill 0 => $self->{child};
250 0         0 return 1;
251             }
252              
253             =head2 DESTROY
254              
255             Destructor. Kills tarantool, removes temporary files.
256              
257             =cut
258              
259             sub DESTROY {
260 9     9   130699 my ($self) = @_;
261 9         88 local $?;
262 9         275 chdir $self->{cwd};
263 9 50       159 return unless $self->{master} == $$;
264              
265 9 50       406 if (-r $self->{core}) {
266 0 0       0 warn "Tarantool was coredumped\n" if -r $self->{core};
267 0         0 system "echo bt|gdb $self->{box} $self->{core}";
268             }
269              
270 9         105 $self->kill;
271 9 50 33     16111 rmtree $self->{temp} if $self->{temp} and !$self->{dont_unlink_temp};
272             }
273              
274              
275             sub temp_dir {
276 0     0 0 0 my ($self) = @_;
277 0         0 return $self->{temp};
278             }
279              
280              
281             sub clean_xlogs {
282 0     0 0 0 my ($self) = @_;
283 0 0       0 return unless $self->{temp};
284 0         0 my @xlogs = glob catfile $self->{temp}, '*.xlog';
285 0         0 unlink for @xlogs;
286             }
287              
288             {
289             my %busy_ports;
290              
291             sub _find_free_port {
292              
293 27     27   45 while( 1 ) {
294 27         152 my $port = 10000 + int rand 30000;
295 27 50       88 next if exists $busy_ports{ $port };
296 27 50       290 next unless IO::Socket::INET->new(
    50          
297             Listen => 5,
298             LocalAddr => '127.0.0.1',
299             LocalPort => $port,
300             Proto => 'tcp',
301             (($^O eq 'MSWin32') ? () : (ReuseAddr => 1)),
302             );
303 27         7685 return $busy_ports{ $port } = $port;
304             }
305             }
306             }
307              
308             =head1 COPYRIGHT AND LICENSE
309              
310             Copyright (C) 2011 Dmitry E. Oboukhov <unera@debian.org>
311             Copyright (C) 2011 Roman V. Nikolaev <rshadow@rambler.ru>
312              
313             This program is free software, you can redistribute it and/or
314             modify it under the terms of the Artistic License.
315              
316             =head1 VCS
317              
318             The project is placed git repo on github:
319             L<https://github.com/dr-co/dr-tarantool/>.
320              
321             =cut
322              
323             1;