File Coverage

blib/lib/Couchbase/MockServer.pm
Criterion Covered Total %
statement 85 126 67.4
branch 11 30 36.6
condition 5 16 31.2
subroutine 13 18 72.2
pod 0 6 0.0
total 114 196 58.1


line stmt bran cond sub pod time code
1             package Couchbase::MockServer;
2 2     2   12 use strict;
  2         4  
  2         92  
3 2     2   26 use warnings;
  2         6  
  2         58  
4 2     2   14982 use IO::Socket::INET;
  2         83488  
  2         16  
5 2     2   1388 use Socket;
  2         8  
  2         2498  
6 2     2   2140 use POSIX qw(:errno_h :signal_h :sys_wait_h);
  2         15448  
  2         74  
7 2     2   6200 use Log::Fu { level => "warn" };
  2         77940  
  2         18  
8 2     2   704 use Data::Dumper;
  2         4  
  2         116  
9 2     2   2070 use Time::HiRes qw(sleep);
  2         3570  
  2         10  
10              
11             my $SYMLINK = "CouchbaseMock_PLTEST.jar";
12             our $INSTANCE;
13              
14             use Class::XSAccessor {
15 2         18 constructor => '_real_new',
16             accessors => [qw(
17             pid
18             jarfile
19             nodes
20             buckets
21             vbuckets
22             harakiri_socket
23             port
24             )]
25 2     2   2052 };
  2         6166  
26             # This is the couchbase mock server, it will attempt to download, spawn, and
27             # otherwise control the java-based CouchbaseMock server.
28              
29              
30             sub _accept_harakiri {
31 1     1   7 my $self = shift;
32 1         104 $self->harakiri_socket->blocking(0);
33 1         66 my $begin_time = time();
34 1         5 my $max_wait = 5;
35 1         2 my $got_accept = 0;
36            
37 1         15 while(time - $begin_time < $max_wait) {
38 48         1051 my $sock = $self->harakiri_socket->accept();
39 48 50       21254 if($sock) {
40 0         0 $sock->blocking(1);
41 0         0 $self->harakiri_socket($sock);
42 0         0 $got_accept = 1;
43 0         0 log_info("Got harakiri connection");
44 0         0 my $buf = "";
45 0         0 $self->harakiri_socket->recv($buf, 100, 0);
46 0 0       0 if(defined $buf) {
47 0         0 my ($port) = ($buf =~ /(\d+)/);
48 0         0 $self->port($port);
49             } else {
50 0         0 die("Couldn't get port");
51             }
52 0         0 last;
53             } else {
54 48         4814918 sleep(0.1);
55             }
56             }
57 1 50       18 if(!$got_accept) {
58 1         37 die("Could not establish harakiri control connection");
59             }
60             }
61              
62             sub _do_run {
63 2     2   6 my $self = shift;
64 2         6 my @command;
65 2         10 push @command, "java", "-jar", $self->jarfile;
66            
67 2         6 my $buckets_arg = "--buckets=";
68            
69 2         4 foreach my $bucket (@{$self->buckets}) {
  2         12  
70 2         8 my ($name,$password,$type) = @{$bucket}{qw(name password type)};
  2         10  
71 2   50     8 $name ||= "";
72 2   50     16 $password ||= "";
73 2   50     16 $type ||= "";
74 2 50 33     24 if($type && $type ne "couchbase" && $type ne "memcache") {
      33        
75 0         0 die("type for bucket must be either 'couchbase' or 'memcache'");
76             }
77 2         8 my $spec = join(":", $name, $password, $type);
78 2         14 $buckets_arg .= $spec . ",";
79             }
80            
81 2         14 $buckets_arg =~ s/,$//g;
82            
83 2         6 push @command, $buckets_arg;
84            
85 2         6 push @command, "--port=0";
86            
87 2 50       26 if($self->nodes) {
88 2         16 push @command, "--nodes=" . $self->nodes;
89             }
90            
91 2         26 my $sock = IO::Socket::INET->new(Listen => 5);
92 2         1102 $self->harakiri_socket($sock);
93 2         16 my $port = $self->harakiri_socket->sockport;
94 2         100 log_infof("Listening on %d for harakiri", $port);
95 2         98 push @command, "--harakiri-monitor=localhost:$port";
96            
97 2         3991 my $pid = fork();
98            
99 2 100       289 if($pid) {
100             #Parent: setup harakiri monitoring socket
101 1         50202 sleep(0.05);
102 1 50       48 if(waitpid($pid, WNOHANG) > 0) {
103 0         0 die("Child process died prematurely");
104             }
105 1         52 log_info("Launched CouchbaseMock PID=$pid");
106             #$self->pid(getpgrp($pid));
107 1         111 $self->pid($pid);
108 1         23 $self->_accept_harakiri();
109             } else {
110            
111 1         154 setpgrp(0, 0);
112 1         181 log_warnf("Executing %s", join(" ", @command));
113 1         0 exec(@command);
114 0         0 warn"exec @command failed: $!";
115 0         0 exit(1);
116             }
117             }
118              
119             sub new {
120 2     2 0 8 my ($cls,%opts) = @_;
121 2 50       14 if($INSTANCE) {
122 0         0 log_warn("Returning cached instance");
123 0         0 return $INSTANCE;
124             }
125            
126 2 50       16 unless(exists $opts{jarfile}) {
127 0         0 die("Must have path to JAR");
128             }
129 2         28 my $o = $cls->_real_new(%opts);
130 2         12 my $file = $o->jarfile;
131 2 50       72 if(!-e $file) {
132 0         0 die("Cannot find $file");
133             }
134              
135 2         12 $o->_do_run();
136 0         0 $INSTANCE = $o;
137 0         0 return $o;
138             }
139              
140             sub GetInstance {
141 0     0 0 0 my $cls = shift;
142 0         0 return $INSTANCE;
143             }
144              
145             sub suspend_process {
146 0     0 0 0 my $self = shift;
147 0         0 my $pid = $self->pid;
148 0 0       0 return unless defined $pid;
149 0         0 kill SIGSTOP, -(getpgrp($pid));
150             }
151             sub resume_process {
152 0     0 0 0 my $self = shift;
153 0         0 my $pid = $self->pid;
154 0 0       0 return unless defined $pid;
155 0         0 kill SIGCONT, -(getpgrp($pid));
156             }
157              
158             sub failover_node {
159 0     0 0 0 my ($self,$nodeidx,$bucket_name) = @_;
160 0   0     0 $bucket_name ||= "default";
161 0         0 my $cmd = "failover,$nodeidx,$bucket_name\n";
162 0         0 log_warn($cmd);
163 0 0       0 $self->harakiri_socket->send($cmd, 0) or die "Couldn't send";
164             }
165              
166             sub respawn_node {
167 0     0 0 0 my ($self,$nodeidx,$bucket_name) = @_;
168 0   0     0 $bucket_name ||= "default";
169 0         0 my $cmd = "respawn,$nodeidx,$bucket_name\n";
170 0         0 log_warn($cmd);
171 0 0       0 $self->harakiri_socket->send($cmd, 0) or die "Couldn't send";
172             }
173              
174             sub DESTROY {
175 1     1   51 my $self = shift;
176 1 50       11 return unless $self->pid;
177 1         31 kill SIGTERM, $self->pid;
178 1         17 log_debugf("Waiting for process to terminate");
179 1         92 waitpid($self->pid, 0);
180 1         25 log_infof("Reaped PID %d, status %d", $self->pid, $? >> 8);
181            
182             }
183              
184             1;