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   8 use strict;
  2         2  
  2         62  
3 2     2   8 use warnings;
  2         2  
  2         38  
4 2     2   1054 use IO::Socket::INET;
  2         37150  
  2         12  
5 2     2   830 use Socket;
  2         2  
  2         1338  
6 2     2   984 use POSIX qw(:errno_h :signal_h :sys_wait_h);
  2         9464  
  2         30  
7 2     2   3584 use Log::Fu { level => "warn" };
  2         48622  
  2         14  
8 2     2   478 use Data::Dumper;
  2         4  
  2         104  
9 2     2   1034 use Time::HiRes qw(sleep);
  2         2396  
  2         6  
10              
11             my $SYMLINK = "CouchbaseMock_PLTEST.jar";
12             our $INSTANCE;
13              
14             use Class::XSAccessor {
15 2         12 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   1200 };
  2         3744  
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   6 my $self = shift;
32 1         62 $self->harakiri_socket->blocking(0);
33 1         49 my $begin_time = time();
34 1         5 my $max_wait = 5;
35 1         3 my $got_accept = 0;
36            
37 1         9 while(time - $begin_time < $max_wait) {
38 42         973 my $sock = $self->harakiri_socket->accept();
39 42 50       11819 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 42         4207261 sleep(0.1);
55             }
56             }
57 1 50       15 if(!$got_accept) {
58 1         61 die("Could not establish harakiri control connection");
59             }
60             }
61              
62             sub _do_run {
63 2     2   4 my $self = shift;
64 2         2 my @command;
65 2         8 push @command, "java", "-jar", $self->jarfile;
66            
67 2         2 my $buckets_arg = "--buckets=";
68            
69 2         4 foreach my $bucket (@{$self->buckets}) {
  2         8  
70 2         2 my ($name,$password,$type) = @{$bucket}{qw(name password type)};
  2         6  
71 2   50     6 $name ||= "";
72 2   50     12 $password ||= "";
73 2   50     6 $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         6 my $spec = join(":", $name, $password, $type);
78 2         8 $buckets_arg .= $spec . ",";
79             }
80            
81 2         10 $buckets_arg =~ s/,$//g;
82            
83 2         4 push @command, $buckets_arg;
84            
85 2         4 push @command, "--port=0";
86            
87 2 50       8 if($self->nodes) {
88 2         10 push @command, "--nodes=" . $self->nodes;
89             }
90            
91 2         20 my $sock = IO::Socket::INET->new(Listen => 5);
92 2         556 $self->harakiri_socket($sock);
93 2         12 my $port = $self->harakiri_socket->sockport;
94 2         74 log_infof("Listening on %d for harakiri", $port);
95 2         76 push @command, "--harakiri-monitor=localhost:$port";
96            
97 2         2053 my $pid = fork();
98            
99 2 100       99 if($pid) {
100             #Parent: setup harakiri monitoring socket
101 1         50311 sleep(0.05);
102 1 50       88 if(waitpid($pid, WNOHANG) > 0) {
103 0         0 die("Child process died prematurely");
104             }
105 1         38 log_info("Launched CouchbaseMock PID=$pid");
106             #$self->pid(getpgrp($pid));
107 1         96 $self->pid($pid);
108 1         23 $self->_accept_harakiri();
109             } else {
110            
111 1         55 setpgrp(0, 0);
112 1         87 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 6 my ($cls,%opts) = @_;
121 2 50       8 if($INSTANCE) {
122 0         0 log_warn("Returning cached instance");
123 0         0 return $INSTANCE;
124             }
125            
126 2 50       8 unless(exists $opts{jarfile}) {
127 0         0 die("Must have path to JAR");
128             }
129 2         18 my $o = $cls->_real_new(%opts);
130 2         10 my $file = $o->jarfile;
131 2 50       26 if(!-e $file) {
132 0         0 die("Cannot find $file");
133             }
134              
135 2         10 $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       12 return unless $self->pid;
177 1         27 kill SIGTERM, $self->pid;
178 1         8 log_debugf("Waiting for process to terminate");
179 1         139 waitpid($self->pid, 0);
180 1         13 log_infof("Reaped PID %d, status %d", $self->pid, $? >> 8);
181            
182             }
183              
184             1;