File Coverage

lib/Consul/Simple.pm
Criterion Covered Total %
statement 130 132 98.4
branch 27 38 71.0
condition 11 17 64.7
subroutine 16 17 94.1
pod 4 4 100.0
total 188 208 90.3


line stmt bran cond sub pod time code
1             package Consul::Simple;
2             $Consul::Simple::VERSION = '1.142430';
3 10     10   8308 use strict;use warnings;
  10     10   18  
  10         384  
  10         76  
  10         16  
  10         324  
4 10     10   22424 use LWP::UserAgent;
  10         656594  
  10         458  
5 10     10   126 use HTTP::Response;
  10         24  
  10         208  
6 10     10   10910 use HTTP::Request::Common;
  10         23628  
  10         970  
7 10     10   9570 use MIME::Base64;
  10         10374  
  10         636  
8 10     10   70 use JSON;
  10         20  
  10         100  
9 10     10   1566 use Data::Dumper;
  10         20  
  10         13798  
10              
11             sub new {
12 6     6 1 1724 my $class = shift;
13 6         34 my $self = {};
14 6         52 bless ($self,$class);
15 6         23 my %args;
16 6         22 { my @args = @_;
  6         52  
17 6 50       45 die 'Consul::Simple::new: even number of arguments required'
18             if scalar @args % 2;
19 6         47 %args = @args;
20             }
21 6   100     146 $self->{consul_server} = $args{consul_server} || 'localhost';
22 6 50       43 $args{kv_prefix} = $args{kvPrefix} if $args{kvPrefix};
23 6   100     47 $self->{kv_prefix} = $args{kv_prefix} || '/';
24 6         188 $self->{kv_prefix} =~ s/\/\//\//g;
25 6         16 $self->{kv_prefix} =~ s/\/\//\//g;
26 6         18 $self->{kv_prefix} =~ s/\/\//\//g;
27 6 100       53 if($self->{kv_prefix} !~ /^\//) {
28 4         27 $self->{kv_prefix} = '/' . $self->{kv_prefix};
29             }
30 6 100       33 if($self->{kv_prefix} !~ /\/$/) {
31 4         14 $self->{kv_prefix} = $self->{kv_prefix} . '/';
32             }
33 6         13 { my %ua_args = ();
  6         19  
34 6 50       20 $ua_args{ssl_opts} = $args{ssl_opts} if $args{ssl_opts};
35 6         141 $self->{ua} = LWP::UserAgent->new(%ua_args);
36             }
37              
38 6   50     186880 $self->{proto} = $args{proto} || 'http';
39 6   50     186 $self->{consul_port} = $args{port} || 8500;
40 6         31 $self->{constructor_args} = \%args;
41             $self->{warning_handler} = $args{warning_handler} || sub {
42 13     13   32 my $warnstr = shift;
43 13         54 my %args = @_;
44 13         1599 print STDERR "[WARN]: $warnstr\n";
45 6   50     129 };
46 6         97 return $self;
47             }
48              
49             sub _warn {
50 13     13   147 my $self = shift;
51 13         62 $self->{warning_handler}->(@_);
52             }
53              
54             sub _do_request {
55 16     16   71846 my $self = shift;
56 16         36 my $req = shift;
57 16         39 my %req_args = @_;
58 16         29 my $ret;
59 16   50     238 my $tries = $self->{constructor_args}->{retries} || 5;
60 16   50     107 my $timeout = $self->{constructor_args}->{timeout} || 2;
61 16         58 while($tries--) {
62 27         94 eval {
63 27     0   944 local $SIG{ALRM} = sub { die "timed out\n"; };
  0         0  
64 27         284 alarm $timeout;
65 27         337 $ret = $self->{ua}->request($req, %req_args);
66             };
67 27         2577701 alarm 0;
68 27 100       158 last if $ret->status_line =~ /^404 /;
69 26 100 66     582 last if $ret and $ret->is_success;
70 13         188 my $err;
71 13 50       55 if($@) {
72 0         0 $err = $@;
73             } else {
74 13         51 $err = 'http request failed with ' . $ret->status_line;
75             }
76 13         180 $self->_warn("request failed: $err", response => $ret);
77 13         13006515 sleep 1;
78             }
79 16         286 return $ret;
80             }
81              
82             sub KVGet {
83 6     6 1 22 my $self = shift;
84 6         23 my $key = shift;
85 6 50       30 die 'Consul::Simple::KVGet: key required as first argument' unless defined $key;
86 6         11 my %args;
87 6         11 { my @args = @_;
  6         21  
88 6 50       31 die 'Consul::Simple::KVGet: even number of arguments required'
89             if scalar @args % 2;
90 6         21 %args = @args;
91             }
92 6         16 my @entries = ();
93 6         14 eval {
94 6         27 my $url = $self->_mk_kv_url($key);
95 6 100       25 $url .= '?recurse' if $args{recurse};
96 6         35 my $res = $self->_do_request(GET $url);
97 6         69 my $content = $res->content;
98 6         181 my $values = JSON::decode_json($content);
99 5         69 @entries = @$values;
100             };
101 6         19 my @ret = ();
102 6         21 foreach my $entry (@entries) {
103             #The returned entry Value is always base64 encoded
104 6         56 $entry->{Value} = MIME::Base64::decode_base64($entry->{Value});
105              
106             #the idea below is to try to JSON decode it. If that works,
107             #return the JSON decoded value. Otherwise, return it un-decoded
108 6         9 my $value;
109 6         13 eval {
110 6         65 $value = JSON::decode_json($entry->{Value});
111             };
112 6 100       24 $value = $entry->{Value} unless $value;
113 6         14 $entry->{Value} = $value;
114 6         17 push @ret, $entry;
115             }
116              
117 6         65 return @ret;
118             }
119              
120              
121             sub KVPut {
122 6     6 1 20 my $self = shift;
123 6         31 my $key = shift;
124 6 50       43 die 'Consul::Simple::KVPut: key required as first argument' unless defined $key;
125 6 50       27 my $value = shift or die 'Consul::Simple::KVPut: value required as second argument';
126 6 100       26 if(ref $value) {
127 3         81 $value = JSON::encode_json($value);
128             }
129 6         16 my %args;
130 6         13 { my @args = @_;
  6         13  
131 6 50       30 die 'Consul::Simple::KVPut: even number of arguments required'
132             if scalar @args % 2;
133 6         30 %args = @args;
134             }
135 6         33 my $res = $self->_do_request(PUT $self->_mk_kv_url($key), Content => $value);
136 6         82 return $res;
137             }
138              
139             sub _mk_kv_url {
140 16     16   38 my $self = shift;
141 16         30 my $key = shift;
142 16         159 return $self->{proto} . '://' . $self->{consul_server} . ':' . $self->{consul_port} . '/v1/kv' . $self->{kv_prefix} . $key;
143             }
144              
145             sub KVDelete {
146 4     4 1 3057 my $self = shift;
147 4         12 my $key = shift;
148 4 50       19 die 'Consul::Simple::KVDelete: key required as first argument' unless defined $key;
149 4         11 my %args;
150 4         10 { my @args = @_;
  4         81  
151 4 50       22 die 'Consul::Simple::KVPut: even number of arguments required'
152             if scalar @args % 2;
153 4         16 %args = @args;
154             }
155 4         20 my $url = $self->_mk_kv_url($key);
156 4 100       21 $url .= '?recurse' if $args{recurse};
157 4         35 my $res = $self->_do_request(
158             HTTP::Request::Common::_simple_req(
159             'DELETE',
160             $url
161             )
162             );
163 4         57 return $res;
164             }
165             1;
166              
167             __END__