File Coverage

blib/lib/HealthCheck/Diagnostic/RabbitMQ.pm
Criterion Covered Total %
statement 100 101 99.0
branch 46 50 92.0
condition 40 44 90.9
subroutine 11 11 100.0
pod 3 3 100.0
total 200 209 95.6


line stmt bran cond sub pod time code
1             package HealthCheck::Diagnostic::RabbitMQ;
2              
3             # ABSTRACT: Check connectivity and queues on a RabbitMQ server
4 1     1   86173 use version;
  1         1652  
  1         5  
5             our $VERSION = 'v1.2.0'; # VERSION
6              
7 1     1   90 use 5.010;
  1         6  
8 1     1   4 use strict;
  1         2  
  1         16  
9 1     1   4 use warnings;
  1         2  
  1         22  
10 1     1   381 use parent 'HealthCheck::Diagnostic';
  1         229  
  1         5  
11              
12 1     1   2175 use Carp;
  1         3  
  1         808  
13              
14             sub new {
15 7     7 1 2666 my ($class, @params) = @_;
16              
17             # Allow either a hashref or even-sized list of params
18             my %params = @params == 1 && ( ref $params[0] || '' ) eq 'HASH'
19 7 50 33     34 ? %{ $params[0] } : @params;
  0         0  
20              
21 7         33 return $class->SUPER::new(
22             label => 'rabbit_mq',
23             %params
24             );
25             }
26              
27             sub check {
28 30     30 1 11880 my ( $self, %params ) = @_;
29              
30             # The method the object needs to have for us to proceed
31 30         57 my $method = 'get_server_properties';
32              
33             # These are the params that we actually use to make our decisions
34             # and that we're going to return in the result to make that clear.
35 30         68 my %decision_params = ( rabbit_mq => undef );
36              
37 30         74 my @limits = qw(
38             listeners_min_critical
39             listeners_min_warning
40             listeners_max_critical
41             listeners_max_warning
42              
43             messages_critical
44             messages_warning
45             );
46              
47             # If we have a queue to check, that changes our requirements
48 30 100 100     113 if ( defined $params{queue}
      100        
49             or ( ref $self and defined $self->{queue} ) )
50             {
51 19         29 $method = 'queue_declare';
52 19         91 $decision_params{$_} = undef for qw(
53             queue
54             channel
55             ), @limits;
56             }
57              
58             # Now we prefer the params passed to check,
59             # and fall back to what is on the instance.
60 30         88 foreach my $param ( keys %decision_params ) {
61             $decision_params{$param}
62             = exists $params{$param} ? $params{$param}
63 182 100       344 : ref $self ? $self->{$param}
    100          
64             : undef;
65             }
66              
67             # No need to return the limits we aren't using in the result
68 30         58 delete @decision_params{ grep { !defined $decision_params{$_} } @limits };
  180         351  
69              
70             # The rabbit_mq param was only "known" so we could choose between
71             # one that was passed to check and the one on the instance.
72 30         58 my $rabbit_mq = delete $decision_params{rabbit_mq};
73 30         52 my $should_disconnect = 0;
74 30 100       74 if (ref $rabbit_mq eq 'CODE') {
75 9         12 local $@;
76 9         17 ($rabbit_mq, $should_disconnect) = eval {
77 9         23 local $SIG{__DIE__};
78 9         25 $rabbit_mq->(%params);
79             };
80 9 100       69 if ($@) {
81 1         5 return $self->summarize({ status => 'CRITICAL', info => "$@" })
82             }
83             }
84              
85 29 100 100     811 croak("'rabbit_mq' must have '$method' method") unless $rabbit_mq and do {
86 22         39 local $@; eval { local $SIG{__DIE__}; $rabbit_mq->can($method) } };
  22         38  
  22         57  
  22         283  
87              
88             # Any "should_disconnect" in the params or the instance should take
89             # precedence over what might have been returned by a coderef:
90             #
91             $should_disconnect = exists $params{should_disconnect}
92             ? $params{should_disconnect}
93             : (ref $self && exists $self->{should_disconnect})
94             ? $self->{should_disconnect}
95 20 50 66     65 : $should_disconnect;
    50          
96              
97             # In theory we could default to random channel in the
98             # range of 1..$rabbit_mq->get_max_channel
99             # but then we would have to:
100             # 1. Hope it's not in use
101             # 2. Open and then close it.
102             # Instead we default to 1 as that's what our internal code does.
103             $decision_params{channel} //= 1
104 20 100 100     71 if exists $decision_params{channel};
105              
106 20         72 my $res = $self->SUPER::check(
107             %params,
108             %decision_params,
109             rabbit_mq => $rabbit_mq,
110             should_disconnect => $should_disconnect,
111             );
112              
113             # Make sure we report what we actually *used*
114             # not what our parent may have copied out of %{ $self }
115 20 100       940 $res->{data} = { %{ $res->{data} || {} }, %decision_params }
  16 100       80  
116             if %decision_params;
117 20         45 delete $res->{rabbit_mq}; # don't include the object in the result
118              
119 20         155 return $res;
120             }
121              
122             sub run {
123 20     20 1 463 my ( $self, %params ) = @_;
124 20         36 my $rabbit_mq = $params{rabbit_mq};
125              
126 20     4   64 my $cb = sub { $rabbit_mq->get_server_properties };
  4         10  
127              
128 20 100       43 if ( defined $params{queue} ) {
129 16         23 my $queue = $params{queue};
130 16         24 my $channel = $params{channel};
131              
132             $cb = sub {
133 16     16   52 my ( $name, $messages, $listeners )
134             = $rabbit_mq->queue_declare( $channel, $queue,
135             { passive => 1 } );
136              
137             return {
138 14         144 name => $name,
139             messages => $messages,
140             listeners => $listeners,
141             };
142 16         64 };
143             }
144              
145 20         32 my $data;
146             {
147 20         25 local $@;
  20         26  
148 20         30 eval {
149 20         42 local $SIG{__DIE__};
150 20         37 $data = $cb->();
151 16 50       57 $rabbit_mq->disconnect if $params{should_disconnect};
152             };
153              
154 20 100       757 if ( my $e = $@ ) {
155 4         7 my $file = quotemeta __FILE__;
156 4         56 $e =~ s/ at $file line \d+\.?\n\Z//ms;
157 4         12 $e =~ s/^Declaring queue: //;
158 4         34 return { status => 'CRITICAL', info => $e };
159             }
160             }
161              
162 16         41 my %res = ( status => 'OK', data => $data );
163              
164 16 100       35 if ( defined $data->{listeners} ) {
165 14         21 my $listeners = $data->{listeners};
166 14 100 100     96 if (( defined $params{listeners_max_critical}
    100 100        
      100        
      100        
      100        
      66        
167             and $params{listeners_max_critical} <= $listeners
168             )
169             or ( defined $params{listeners_min_critical}
170             and $params{listeners_min_critical} >= $listeners )
171             )
172             {
173 4         8 my $min = $params{listeners_min_critical};
174 4         7 my $max = $params{listeners_max_critical};
175 4         7 $res{status} = 'CRITICAL';
176 4         6 my $info = "Listeners out of range! Expected";
177 4 100       10 $info .= " min: $min" if defined $min;
178 4 100       9 $info .= " max: $max" if defined $max;
179 4         10 $res{info} = "$info have: $listeners";
180             }
181             elsif (
182             ( defined $params{listeners_max_warning}
183             and $params{listeners_max_warning} <= $listeners
184             )
185             or ( defined $params{listeners_min_warning}
186             and $params{listeners_min_warning} >= $listeners )
187             )
188             {
189 2         4 my $min = $params{listeners_min_warning};
190 2         3 my $max = $params{listeners_max_warning};
191 2         4 $res{status} = 'WARNING';
192 2         4 my $info = "Listeners out of range! Expected";
193 2 100       6 $info .= " min: $min" if defined $min;
194 2 100       5 $info .= " max: $max" if defined $max;
195 2         5 $res{info} = "$info have: $listeners";
196             }
197             }
198              
199 16 100 100     50 if ( $res{status} ne 'CRITICAL' and defined $data->{messages} ) {
200 10         26 my $messages = $data->{messages};
201 10 100 100     38 if ( defined $params{messages_critical}
    100 100        
202             and $params{messages_critical} <= $messages )
203             {
204 2         3 $res{status} = 'CRITICAL';
205             $res{info} = sprintf(
206             "Messages out of range! Expected max: %d have: %d",
207             $params{messages_critical},
208 2         10 $messages,
209             );
210             }
211             elsif ( defined $params{messages_warning}
212             and $params{messages_warning} <= $messages )
213             {
214 1         3 $res{status} = 'WARNING';
215             $res{info} = sprintf(
216             "Messages out of range! Expected max: %d have: %d",
217             $params{messages_warning},
218 1         5 $messages,
219             );
220             }
221             }
222              
223 16         80 return \%res;
224             }
225              
226             1;
227              
228             __END__