File Coverage

blib/lib/Csistck/Test/Pkg.pm
Criterion Covered Total %
statement 68 104 65.3
branch 11 24 45.8
condition 3 8 37.5
subroutine 16 18 88.8
pod 4 9 44.4
total 102 163 62.5


line stmt bran cond sub pod time code
1             package Csistck::Test::Pkg;
2              
3 17     17   365 use 5.010;
  17         51  
4 17     17   79 use strict;
  17         25  
  17         378  
5 17     17   72 use warnings;
  17         27  
  17         556  
6              
7 17     17   71 use base 'Csistck::Test';
  17         31  
  17         1565  
8 17     17   84 use Csistck::Oper qw/debug/;
  17         27  
  17         790  
9 17     17   131 use Csistck::Config qw/option/;
  17         24  
  17         932  
10              
11             our @EXPORT_OK = qw/pkg/;
12              
13 17     17   75 use Digest::MD5;
  17         22  
  17         533  
14 17     17   69 use File::Basename;
  17         26  
  17         1418  
15              
16             # Conditionally use linux-only modules
17             BEGIN {
18 17 50   17   108 if ("$^O" eq "linux") {
19 17         8056 require Linux::Distribution;
20             }
21             }
22              
23             our $Cmds = {
24             dpkg => {
25             check => 'dpkg -L "%s"',
26             diff => 'apt-get -s install "%s"',
27             install => 'apt-get -qq -y install "%s"'
28             },
29             rpm => {
30             check => 'rpm -q "%s"',
31             install => 'yum -q -y --noplugins install "%s"'
32             },
33             emerge => {
34             check => 'equery -qC list "%s"',
35             diff => 'emerge --color n -pq "%s"',
36             install => 'emerge --color n -q "%s"'
37             },
38             pacman => {
39             check => 'pacman -Qe "%s"',
40             install => 'pacman -Sq --noconfirm "%s"'
41             },
42             pkg_info => {
43             check => 'pkg_info -Qq "%s>0"'
44             }
45             };
46              
47             =head1 NAME
48              
49             Csistck::Test::Pkg - Csistck package check
50              
51             =head1 METHODS
52              
53             =head2 pkg($package, [$type])
54              
55             Test for existing package using forks to system package managers. Package can be
56             specified as a string, or as a hashref:
57              
58             pkg({
59             dpkg => 'test-server',
60             emerge => 'net-test',
61             default => 'test-server'
62             });
63              
64             The package manager will be automatically detected if none is explicitly
65             specified, and the hashref key matching the package manager decides the package
66             name to check. If a default key is provided, that package is used by default.
67              
68             In repair mode, install the package quietly, unless package manager doesn't
69             handle automating install.
70              
71             Supported package managers:
72              
73             =over
74              
75             =item dpkg
76              
77             Debian package management utility
78              
79             =item pacman
80              
81             Arch linux package management utility
82              
83             =item More planned..
84              
85             =back
86              
87             =cut
88              
89             sub pkg {
90 5     5 1 460 my $pkg = shift;
91 5   50     23 my $type = shift // undef;
92 5         30 my %args = (
93             type => $type,
94             @_
95             );
96 5         36 Csistck::Test::Pkg->new($pkg, %args);
97             }
98              
99             sub new {
100 10     10 0 629 my $class = shift;
101 10         80 my $self = $class->SUPER::new(@_);
102 10         21 bless($self, $class);
103              
104             # We'll fix package name here, instead of redoing each call
105             # Priority: 'type' argument, 'pkg_type' option, detect_pkg_manager.
106 10         36 my $type = $self->{type};
107 10 50       39 if (! $type) {
108 0   0     0 $type = option('pkg_type') // detect_pkg_manager();
109             }
110 10 50       37 return $self->fail("Unsupported package manager or OS: manager=")
111             if (! $type);
112             return $self->fail("Package manager not supported: type=<$type>")
113 10 50       35 if (! $Cmds->{$type});
114 10         18 $self->{type} = $type;
115              
116 10         44 return $self;
117             }
118              
119             sub desc {
120 8     8 0 47 return sprintf("Package test for %s, using %s",
121             $_[0]->pkg_name, $_[0]->pkg_type);
122             }
123              
124             sub check {
125 4     4 0 766 my $self = shift;
126 4         11 my $pkg = $self->pkg_name;
127 4         9 my $type = $self->pkg_type;
128 4 50       375 my $cmd = sprintf($Cmds->{$type}->{check}, $pkg) or
129             return $self->fail("Package check command missing: type=<$type>");
130              
131 4         39 debug("Searching for package via command: cmd=<$cmd>");
132 4         1196445 my $ret = system("$cmd 1>/dev/null 2>/dev/null");
133              
134 4 100       112 return $self->fail("Package missing")
135             unless($ret == 0);
136              
137 3         71 return $self->pass("Package installed");
138             }
139              
140             sub repair {
141 1     1 0 5 my $self = shift;
142 1         5 my $pkg = $self->pkg_name;
143 1         5 my $type = $self->pkg_type;
144 1         2 my $cmd;
145              
146 1 50       7 if (defined $Cmds->{$type}->{install}) {
147 1         91 $cmd = sprintf($Cmds->{$type}->{install}, $pkg);
148             }
149             else {
150 0         0 return $self->fail("Package install command missing: type=<$type>");
151             }
152              
153 1         20 $ENV{DEBIAN_FRONTEND} = "noninteractive";
154 1         8 debug("Installing package via command: cmd=<$cmd>");
155 1         3026 my $ret = system("$cmd 1>/dev/null 2>/dev/null");
156              
157 1 50       23 return $self->fail("Package installation failed")
158             unless ($ret == 0);
159            
160 1         18 return $self->pass("Package installation successful");
161             }
162              
163             # Package diff
164             sub diff {
165 0     0 0 0 my $self = shift;
166 0         0 my $pkg = $self->pkg_name;
167 0         0 my $type = $self->pkg_type;
168 0         0 my $cmd;
169            
170 0 0       0 if (defined $Cmds->{$type}->{diff}) {
171 0         0 $cmd = sprintf($Cmds->{$type}->{diff}, $pkg);
172             }
173             else {
174 0         0 return $self->fail("Package diff command missing: type=<$type>");
175             }
176            
177 0         0 $ENV{DEBIAN_FRONTEND} = "noninteractive";
178 0         0 debug("Showing package differences via command: cmd=<$cmd>");
179 0         0 my $ret = system("$cmd 2>/dev/null");
180              
181 0 0       0 return $self->fail("Package differences query failed")
182             unless ($ret == 0);
183             }
184              
185             =head2 detect_pkg_manager()
186              
187             Detect package manager based on system OS and Linux distribution if
188             applicable. Return package manager as string. This is not exported, it is
189             used for the package test.
190              
191             =cut
192              
193             sub detect_pkg_manager {
194 0     0 1 0 my $self = shift;
195 0         0 given ("$^O") {
196 0         0 when (/^freebsd$/) { return 'pkg_info'; }
  0         0  
197 0         0 when (/^netbsd$/) { return 'pkg_info'; }
  0         0  
198 0         0 when (/^linux$/) {
199 0         0 given (Linux::Distribution::distribution_name()) {
200 0         0 when (/^(?:debian|ubuntu)$/) { return 'dpkg'; }
  0         0  
201 0         0 when (/^(?:fedora|redhat|centos)$/) { return 'rpm'; }
  0         0  
202 0         0 when (/^gentoo$/) { return 'emerge'; }
  0         0  
203 0         0 when (/^arch$/) { return 'pacman'; }
  0         0  
204 0         0 default { return undef; }
  0         0  
205             }
206             }
207 0         0 when (/^darwin$/) { return undef; }
  0         0  
208 0         0 default { return undef; }
  0         0  
209             }
210             }
211              
212             =head2 pkg_name($package, $type)
213              
214             Based on input package, return package name. With OS and distribution detection,
215             $package can be passed as a string or a hashref.
216              
217             See pkg() for information on passing hashrefs as package name
218              
219             =cut
220              
221             sub pkg_name {
222 17     17 1 30 my $self = shift;
223 17         40 my $pkg = $self->{target};
224 17         32 my $type = $self->{type};
225              
226 17         29 given (ref $pkg) {
227 17         40 when ('') {
228 13 50       154 return $pkg if ($pkg =~ m/^[A-Za-z0-9\-\_\.\/]+$/);
229             }
230 4         10 when ('HASH') {
231 4   66     13 my $pkg_name = $pkg->{$type} // $pkg->{default};
232 4 50       50 return $pkg_name if ($pkg_name =~ m/^[A-Za-z0-9\-\_\.\/]+$/);
233             }
234             }
235            
236 0         0 die('Invalid package');
237             }
238              
239             =head2 pkg_type()
240              
241             Return fixed package type
242              
243             =cut
244              
245 13     13 1 121 sub pkg_type { $_[0]->{type}; }
246              
247             1;
248             __END__