File Coverage

blib/lib/Csistck/Test/Pkg.pm
Criterion Covered Total %
statement 69 105 65.7
branch 11 24 45.8
condition 3 8 37.5
subroutine 16 18 88.8
pod 4 9 44.4
total 103 164 62.8


line stmt bran cond sub pod time code
1             package Csistck::Test::Pkg;
2              
3 17     17   514 use 5.010;
  17         60  
  17         704  
4 17     17   107 use strict;
  17         76  
  17         596  
5 17     17   94 use warnings;
  17         29  
  17         694  
6              
7 17     17   88 use base 'Csistck::Test';
  17         36  
  17         1855  
8 17     17   97 use Csistck::Oper qw/debug/;
  17         29  
  17         877  
9 17     17   94 use Csistck::Config qw/option/;
  17         33  
  17         1034  
10              
11             our @EXPORT_OK = qw/pkg/;
12              
13 17     17   101 use Digest::MD5;
  17         38  
  17         664  
14 17     17   106 use File::Basename;
  17         32  
  17         1602  
15              
16             # Conditionally use linux-only modules
17             BEGIN {
18 17 50   17   118 if ("$^O" eq "linux") {
19 17         20530 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, :\&on_repair)
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             If a repair operation is run, the on_repair function is called
72              
73             Supported package managers:
74              
75             =over
76              
77             =item dpkg
78              
79             Debian package management utility
80              
81             =item pacman
82              
83             Arch linux package management utility
84              
85             =item More planned..
86              
87             =back
88              
89             =cut
90              
91             sub pkg {
92 5     5 1 831 my $pkg = shift;
93 5   50     35 my $type = shift // undef;
94 5         33 my %args = (
95             type => $type,
96             @_
97             );
98 5         51 Csistck::Test::Pkg->new($pkg, %args);
99             }
100              
101             sub new {
102 10     10 0 1141 my $class = shift;
103 10         109 my $self = $class->SUPER::new(@_);
104 10         30 bless($self, $class);
105              
106             # We'll fix package name here, instead of redoing each call
107             # Priority: 'type' argument, 'pkg_type' option, detect_pkg_manager.
108 10         56 my $type = $self->{type};
109 10 50       49 if (! $type) {
110 0   0     0 $type = option('pkg_type') // detect_pkg_manager();
111             }
112 10 50       46 return $self->fail("Unsupported package manager or OS: manager=")
113             if (! $type);
114 10 50       49 return $self->fail("Package manager not supported: type=<$type>")
115             if (! $Cmds->{$type});
116 10         24 $self->{type} = $type;
117              
118 10         58 return $self;
119             }
120              
121             sub desc {
122 8     8 0 69 return sprintf("Package test for %s, using %s",
123             $_[0]->pkg_name, $_[0]->pkg_type);
124             }
125              
126             sub check {
127 4     4 0 1251 my $self = shift;
128 4         17 my $pkg = $self->pkg_name;
129 4         11 my $type = $self->pkg_type;
130 4 50       28 my $cmd = sprintf($Cmds->{$type}->{check}, $pkg) or
131             return $self->fail("Package check command missing: type=<$type>");
132              
133 4         44 debug("Searching for package via command: cmd=<$cmd>");
134 4         67686 my $ret = system("$cmd 1>/dev/null 2>/dev/null");
135              
136 4 100       181 return $self->fail("Package missing")
137             unless($ret == 0);
138              
139 3         161 return $self->pass("Package installed");
140             }
141              
142             sub repair {
143 1     1 0 3 my $self = shift;
144 1         3 my $pkg = $self->pkg_name;
145 1         3 my $type = $self->pkg_type;
146 1         2 my $cmd;
147              
148 1 50       5 if (defined $Cmds->{$type}->{install}) {
149 1         5 $cmd = sprintf($Cmds->{$type}->{install}, $pkg);
150             }
151             else {
152 0         0 return $self->fail("Package install command missing: type=<$type>");
153             }
154              
155 1         19 $ENV{DEBIAN_FRONTEND} = "noninteractive";
156 1         7 debug("Installing package via command: cmd=<$cmd>");
157 1         21421 my $ret = system("$cmd 1>/dev/null 2>/dev/null");
158              
159 1 50       73 return $self->fail("Package installation failed")
160             unless ($ret == 0);
161            
162 1         49 return $self->pass("Package installation successful");
163             }
164              
165             # Package diff
166             sub diff {
167 0     0 0 0 my $self = shift;
168 0         0 my $pkg = $self->pkg_name;
169 0         0 my $type = $self->pkg_type;
170 0         0 my $cmd;
171            
172 0 0       0 if (defined $Cmds->{$type}->{diff}) {
173 0         0 $cmd = sprintf($Cmds->{$type}->{diff}, $pkg);
174             }
175             else {
176 0         0 return $self->fail("Package diff command missing: type=<$type>");
177             }
178            
179 0         0 $ENV{DEBIAN_FRONTEND} = "noninteractive";
180 0         0 debug("Showing package differences via command: cmd=<$cmd>");
181 0         0 my $ret = system("$cmd 2>/dev/null");
182              
183 0 0       0 return $self->fail("Package differences query failed")
184             unless ($ret == 0);
185             }
186              
187             =head2 detect_pkg_manager()
188              
189             Detect package manager based on system OS and Linux distribution if
190             applicable. Return package manager as string. This is not exported, it is
191             used for the package test.
192              
193             =cut
194              
195             sub detect_pkg_manager {
196 0     0 1 0 my $self = shift;
197 0         0 given ("$^O") {
198 0         0 when (/^freebsd$/) { return 'pkg_info'; }
  0         0  
199 0         0 when (/^netbsd$/) { return 'pkg_info'; }
  0         0  
200 0         0 when (/^linux$/) {
201 0         0 given (Linux::Distribution::distribution_name()) {
202 0         0 when (/^(?:debian|ubuntu)$/) { return 'dpkg'; }
  0         0  
203 0         0 when (/^(?:fedora|redhat|centos)$/) { return 'rpm'; }
  0         0  
204 0         0 when (/^gentoo$/) { return 'emerge'; }
  0         0  
205 0         0 when (/^arch$/) { return 'pacman'; }
  0         0  
206 0         0 default { return undef; }
  0         0  
207             }
208             }
209 0         0 when (/^darwin$/) { return undef; }
  0         0  
210 0         0 default { return undef; }
  0         0  
211             }
212             }
213              
214             =head2 pkg_name($package, $type)
215              
216             Based on input package, return package name. With OS and distribution detection,
217             $package can be passed as a string or a hashref.
218              
219             See pkg() for information on passing hashrefs as package name
220              
221             =cut
222              
223             sub pkg_name {
224 17     17 1 41 my $self = shift;
225 17         66 my $pkg = $self->{target};
226 17         45 my $type = $self->{type};
227              
228 17         233 given (ref $pkg) {
229 17         273 when ('') {
230 13 50       249 return $pkg if ($pkg =~ m/^[A-Za-z0-9\-\_\.\/]+$/);
231             }
232 4         21 when ('HASH') {
233 4   66     118 my $pkg_name = $pkg->{$type} // $pkg->{default};
234 4 50       781 return $pkg_name if ($pkg_name =~ m/^[A-Za-z0-9\-\_\.\/]+$/);
235             }
236             }
237            
238 0         0 die('Invalid package');
239             }
240              
241             =head2 pkg_type()
242              
243             Return fixed package type
244              
245             =cut
246              
247 13     13 1 206 sub pkg_type { $_[0]->{type}; }
248              
249             1;
250             __END__