File Coverage

blib/lib/Couchbase/MockServer.pm
Criterion Covered Total %
statement 27 126 21.4
branch 0 30 0.0
condition 0 16 0.0
subroutine 9 18 50.0
pod 0 6 0.0
total 36 196 18.3


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