File Coverage

blib/lib/Server/Module/Comparison.pm
Criterion Covered Total %
statement 104 157 66.2
branch 28 42 66.6
condition 4 6 66.6
subroutine 15 21 71.4
pod 5 9 55.5
total 156 235 66.3


line stmt bran cond sub pod time code
1 3     3   281591 use strictures 2;
  3         3992  
  3         134  
2             package Server::Module::Comparison;
3              
4             # ABSTRACT: check perl module versions installed on servers.
5              
6 3     3   2887 use Path::Tiny;
  3         28980  
  3         181  
7 3     3   1686 use Moo;
  3         29438  
  3         16  
8 3     3   5410 use Types::Standard -types;
  3         149082  
  3         34  
9 3     3   11248 use Capture::Tiny qw/capture/;
  3         54432  
  3         175  
10 3     3   1202 use failures qw/module::comparison/;
  3         7812  
  3         13  
11 3     3   1500 use Perl::Version;
  3         5465  
  3         3589  
12              
13             our $VERSION = '0.012';
14              
15             has perl_path => (is => 'ro', isa => Str, default => '');
16             has modules => (is => 'ro', isa => ArrayRef);
17              
18             sub FromModuleList
19             {
20 0     0 1 0 my $filename = shift;
21 0         0 my $extra_params = shift;
22 0 0       0 $extra_params = {} unless defined $extra_params;
23 0         0 my @lines;
24 0 0       0 if($filename eq '-')
25             {
26 0         0 @lines = ;
27             }
28             else
29             {
30 0         0 @lines = path($filename)->lines_utf8;
31             }
32 0         0 my @modules = map { chomp; $_ } grep { !/^\s*$/ } @lines;
  0         0  
  0         0  
  0         0  
33 0         0 return Server::Module::Comparison->new({ %$extra_params, modules => \@modules });
34             }
35              
36             sub _mversion_command
37             {
38 1     1   3 my $self = shift;
39 1         1 my $command = 'mversion';
40 1 50       6 if($self->perl_path)
41             {
42 1         8 $command = path($self->perl_path)->child($command);
43             }
44 1         89 return [$command, '-f', @{$self->modules}, '2>&1'];
  1         5  
45             }
46              
47             sub check_container
48             {
49 0     0 1 0 my $self = shift;
50 0         0 my $container = shift;
51 0         0 my $cmd = [qw/docker run --rm -i/, $container, @{$self->_mversion_command}];
  0         0  
52 0         0 return $self->_run_mversion($cmd);
53             }
54              
55             sub check_ssh_server
56             {
57 0     0 1 0 my $self = shift;
58 0         0 my $server = shift;
59 0         0 my $cmd = ['ssh', $server, @{$self->_mversion_command}];
  0         0  
60 0         0 return $self->_run_mversion($cmd);
61             }
62              
63             sub check_local
64             {
65 1     1 1 2108 my $self = shift;
66 1         3 my $cmd = $self->_mversion_command;
67 1         4 return $self->_run_mversion($cmd);
68             }
69              
70             sub identify_resource
71             {
72 4     4 0 2049 my $self = shift;
73 4         7 my $identifier = shift;
74 4 100       24 if($identifier =~ m|docker://(.*)$|)
    100          
    100          
75             {
76 1         7 return ('docker', $1);
77             }
78             elsif($identifier =~ m|ssh://(.*)$|)
79             {
80 1         7 return ('ssh', $1);
81             }
82             elsif($identifier =~ m|/|)
83             {
84             # assume it's docker
85 1         5 return ('docker', $identifier);
86             }
87             else
88             {
89             # assume it's ssh
90 1         10 return ('ssh', $identifier);
91             }
92             }
93              
94             sub check_correct_guess
95             {
96 0     0 1 0 my $self = shift;
97 0         0 my $identifier = shift;
98 0         0 my ($type, $server) = $self->identify_resource($identifier);
99 0 0       0 if($type eq 'ssh')
    0          
100             {
101 0         0 return $self->check_ssh_server($server);
102             }
103             elsif($type eq 'docker')
104             {
105 0         0 return $self->check_container($server);
106             }
107             else
108             {
109 0         0 die 'I don\'t know what to do!';
110             }
111             }
112              
113             sub _run_mversion
114             {
115 1     1   2 my $self = shift;
116 1         2 my $cmd = shift;
117             #print "$cmd\n";
118             my ($stdout, $stderr, $exit) = capture {
119 1     1   3119 system(@$cmd);
120 1         26 };
121 1 50       625 if($exit)
122             {
123 1         8 my $command = join(' ', @$cmd);
124 1         27 failure::module::comparison->throw("Failure running $command: $exit");
125             }
126 0         0 my @lines = map { chomp; $_ } grep { !/^\s*$/ } split(/\r\n|\r|\n/, $stdout);
  0         0  
  0         0  
  0         0  
127 0         0 my %versions = map { _module_pair($_) } @lines;
  0         0  
128 0         0 return \%versions;
129             }
130              
131             sub _module_pair
132             {
133 0     0   0 my $line = shift;
134 0         0 my ($module, $version) = $line =~ /(.*) ((:?\d+(:?\.\d+)*|undef))/;
135 0 0       0 if($module)
136             {
137 0         0 return ($module, $version);
138             }
139             else
140             {
141 0         0 my ($missing_module) = $line =~ /'(.*)' does not seem/;
142 0 0       0 unless($missing_module)
143             {
144 0         0 print "Error: $line\n";
145 0         0 return ("error", "error");
146             }
147 0         0 return ($missing_module, 'missing');
148             }
149             }
150              
151             sub _wrap_version
152             {
153 26     26   24 my $version = shift;
154 26 100 100     106 if($version && ($version eq 'undef' || $version eq 'missing'))
      33        
155             {
156             # this isn't perfect, in fact it may bite us.
157             # I believe that undef does indicate that a module is
158             # there, it's just it doesn't have $VERSION set.
159 4         7 return Perl::Version->new('0');
160             }
161 22         82 return Perl::Version->new($version);
162             }
163              
164             sub difference_report
165             {
166 2     2 0 2057 my $self = shift;
167 2         3 my $first = shift;
168 2         3 my $second = shift;
169              
170 2         2 my %newer;
171             my %older;
172 0         0 my %newly_installed;
173 0         0 my %removed;
174 2         7 for my $module (keys %$first)
175             {
176 16 100       291 if(exists $second->{$module})
177             {
178 13         19 my $v1 = _wrap_version($first->{$module});
179 13         534 my $v2 = _wrap_version($second->{$module});
180 13 100       454 if($v1 > $v2)
    100          
181             {
182 1         32 $older{$module} = [$first->{$module}, $second->{$module}];
183             }
184             elsif($v1 < $v2)
185             {
186 2         58 $newer{$module} = [$first->{$module}, $second->{$module}];
187             }
188             }
189             else
190             {
191 3         5 $removed{$module} = $first->{$module};
192             }
193             }
194 2         30 for my $module (keys %$second)
195             {
196 14 100       17 unless(exists $first->{$module})
197             {
198 1         3 $newly_installed{$module} = $second->{$module};
199             }
200             }
201             return {
202 2         10 updated => \%newer,
203             downgraded => \%older,
204             removed => \%removed,
205             installed => \%newly_installed,
206             };
207             }
208              
209             sub human_readable_report
210             {
211 3     3 0 2920 my $self = shift;
212 3         3 my $report = shift;
213 3         3 my @lines;
214 3 100       3 if(%{$report->{downgraded}})
  3         8  
215             {
216 2         4 push @lines, 'DOWNGRADED Modules', '';
217 2         3 for my $key (sort keys %{$report->{downgraded}})
  2         5  
218             {
219 2         1 my ($v1, $v2) = @{$report->{downgraded}->{$key}};
  2         5  
220 2         7 push @lines, sprintf("%-40s\t%s -> %s", $key, $v1, $v2);
221             }
222 2         3 push @lines, '';
223             }
224 3 100       4 if(%{$report->{removed}})
  3         6  
225             {
226 2         2 push @lines, 'REMOVED Modules', '';
227 2         2 for my $key (sort keys %{$report->{removed}})
  2         6  
228             {
229 6         7 my $v1 = $report->{removed}->{$key};
230 6         10 push @lines, sprintf("%-40s\t%s", $key, $v1);
231             }
232 2         3 push @lines, '';
233             }
234 3 100       3 if(%{$report->{installed}})
  3         5  
235             {
236 2         2 push @lines, 'Installed Modules', '';
237 2         2 for my $key (sort keys %{$report->{installed}})
  2         3  
238             {
239 2         3 my $v1 = $report->{installed}->{$key};
240 2         3 push @lines, sprintf("%-40s\t%s", $key, $v1);
241             }
242 2         3 push @lines, '';
243             }
244 3 100       3 if(%{$report->{updated}})
  3         5  
245             {
246 2         3 push @lines, 'Updated Modules', '';
247 2         5 for my $key (sort keys %{$report->{updated}})
  2         5  
248             {
249 4         3 my ($v1, $v2) = @{$report->{updated}->{$key}};
  4         6  
250 4         7 push @lines, sprintf("%-40s\t%s -> %s", $key, $v1, $v2);
251             }
252 2         2 push @lines, '';
253             }
254 3 100       7 unless(@lines)
255             {
256 1         2 push @lines, 'No differences', '';
257             }
258 3         36 return join "\n", @lines;
259             }
260              
261             sub human_readable_list
262             {
263 0     0 0   my $self = shift;
264 0           my $list = shift;
265              
266 0           my @lines;
267 0           for my $module (keys %$list)
268             {
269 0           push @lines, sprintf("%-40s\t%s", $module, $list->{$module});
270             }
271 0           return join "\n", @lines;
272             }
273              
274             1;
275              
276             __END__