File Coverage

blib/lib/Setup/Unix/Group.pm
Criterion Covered Total %
statement 80 82 97.5
branch 34 46 73.9
condition 16 21 76.1
subroutine 9 9 100.0
pod 3 3 100.0
total 142 161 88.2


line stmt bran cond sub pod time code
1             package Setup::Unix::Group;
2              
3             our $DATE = '2017-07-10'; # DATE
4             our $VERSION = '0.14'; # VERSION
5              
6 4     4   156569 use 5.010;
  4         13  
7 4     4   20 use strict;
  4         8  
  4         74  
8 4     4   18 use warnings;
  4         9  
  4         99  
9 4     4   12049 use Log::ger;
  4         357  
  4         26  
10              
11             require Exporter;
12             our @ISA = qw(Exporter);
13             our @EXPORT_OK = qw(setup_unix_group);
14              
15 4     4   6432 use PerlX::Maybe;
  4         5912  
  4         219  
16 4     4   3003 use Unix::Passwd::File;
  4         99130  
  4         4428  
17              
18             our %SPEC;
19              
20             $SPEC{':package'} = {
21             v => 1.1,
22             summary => 'Setup Unix group (existence)',
23             };
24              
25             my %common_args = (
26             etc_dir => {
27             summary => 'Location of passwd files',
28             schema => ['str*' => {default=>'/etc'}],
29             },
30             group => {
31             schema => 'str*',
32             summary => 'Group name',
33             },
34             );
35              
36             $SPEC{delgroup} = {
37             v => 1.1,
38             summary => 'Delete group',
39             description => <<'_',
40              
41             Fixed state: group does not exist.
42              
43             Fixable state: group exists.
44              
45             _
46             args => {
47             %common_args,
48             },
49             features => {
50             tx => {v=>2},
51             idempotent => 1,
52             },
53             };
54             sub delgroup {
55 249     249 1 4948996 my %args = @_;
56              
57 249   50     2363 my $tx_action = $args{-tx_action} // '';
58 249   50     1822 my $dry_run = $args{-tx_action} // '';
59 249 50       1730 my $group = $args{group} or return [400, "Please specify group"];
60 249 50       3325 $group =~ $Unix::Passwd::File::re_group
61             or return [400, "Invalid group"];
62 249         1799 my %ca = (etc_dir => $args{etc_dir}, group=>$group);
63 249         751 my $res;
64              
65 249 100       1558 if ($tx_action eq 'check_state') {
    50          
66 144         1163 my $res = Unix::Passwd::File::get_group(%ca);
67 144 50 66     133684 return $res unless $res->[0] == 200 || $res->[0] == 404;
68              
69 144 100       2401 return [304, "Group $group already doesn't exist"] if $res->[0] == 404;
70 106 50       1348 log_info("(DRY) Deleting Unix group $group ...") if $dry_run;
71             return [200, "Group $group needs to be deleted", undef, {undo_actions=>[
72 106         5593 [addgroup => {%ca, gid => $res->[2]{gid}}],
73             ]}];
74             } elsif ($tx_action eq 'fix_state') {
75             # we don't want to have to get_group() when fixing state, to reduce
76             # number of read passes to the passwd files
77 105         1053 log_info("Deleting Unix group $group ...");
78 105         1408 return Unix::Passwd::File::delete_group(%ca);
79             }
80 0         0 [400, "Invalid -tx_action"];
81             }
82              
83             $SPEC{addgroup} = {
84             v => 1.1,
85             summary => 'Add group',
86             args => {
87             %common_args,
88             gid => {
89             summary => 'Add with specified GID',
90             description => <<'_',
91              
92             If not specified, will search an unused GID from `min_new_gid` to `max_new_gid`.
93              
94             If specified, will accept non-unique GID (that which has been used by other
95             group).
96              
97             _
98             schema => 'int',
99             },
100             min_gid => {
101             summary => 'Specify range for new GID',
102             description => <<'_',
103              
104             If a free GID between `min_gid` and `max_gid` is not available, an error is
105             returned.
106              
107             Passed to Unix::Passwd::File's `min_new_gid`.
108              
109             _
110             schema => [int => {between=>[0, 65535], default=>1000}],
111             },
112             max_gid => {
113             summary => 'Specify range for new GID',
114             description => <<'_',
115              
116             If a free GID between `min_gid` and `max_gid` is not available, an error is
117             returned.
118              
119             Passed to Unix::Passwd::File's `max_new_gid`.
120              
121             _
122             schema => [int => {between=>[0, 65535], default=>65534}],
123             },
124             },
125             features => {
126             tx => {v=>2},
127             idempotent => 1,
128             },
129             };
130             sub addgroup {
131 259     259 1 7014809 my %args = @_;
132              
133 259   50     2299 my $tx_action = $args{-tx_action} // '';
134 259         1150 my $dry_run = $args{-dry_run};
135 259 50       1839 my $group = $args{group} or return [400, "Please specify group"];
136 259 50       3522 $group =~ $Unix::Passwd::File::re_group
137             or return [400, "Invalid group"];
138 259         1191 my $gid = $args{gid};
139 259   100     1359 my $min_gid = $args{min_gid} // 1000;
140 259   100     1590 my $max_gid = $args{max_gid} // 65534;
141 259         1513 my %ca0 = (etc_dir => $args{etc_dir});
142 259         1451 my %ca = (%ca0, group=>$group);
143 259         879 my $res;
144              
145 259 100       1676 if ($tx_action eq 'check_state') {
    50          
146 139         1010 $res = Unix::Passwd::File::get_group(%ca);
147 139 50 66     114871 return $res unless $res->[0] == 200 || $res->[0] == 404;
148              
149 139 100       742 if ($res->[0] == 200) {
150 15 100 100     143 if (!defined($gid) || $gid == $res->[2]{gid}) {
151 13         452 return [304, "Group $group already exists"];
152             } else {
153 2         43 return [412, "Group $group already exists but with different ".
154             "GID ($res->[2]{gid}, wanted $gid)"];
155             }
156             } else {
157 124 50       730 log_info("(DRY) Adding Unix group $group ...") if $dry_run;
158 124         9675 return [200, "Group $group needs to be added", undef,
159             {undo_actions=>[
160             [delgroup => {%ca}],
161             ]}];
162             }
163             } elsif ($tx_action eq 'fix_state') {
164             # we don't want to have to get_group() when fixing state, to reduce
165             # number of read passes to the passwd files
166 120         1292 log_info("Adding Unix group $group ...");
167 120         1655 $res = Unix::Passwd::File::add_group(
168             %ca,
169             maybe gid => $gid,
170             min_gid => $min_gid,
171             max_gid => $max_gid);
172 120 100       228545 if ($res->[0] == 200) {
173 118         1085 $args{-stash}{result}{gid} = $res->[2]{gid};
174 118         5598 return [200, "Created"];
175             } else {
176 2         92 return $res;
177             }
178             }
179 0         0 [400, "Invalid -tx_action"];
180             }
181              
182             $SPEC{setup_unix_group} = {
183             v => 1.1,
184             summary => "Setup Unix group (existence)",
185             description => <<'_',
186              
187             On do, will create Unix group if not already exists. The created GID will be
188             returned in the result (`{gid => GID}`). If `should_already_exist` is set to
189             true, won't create but only require that group already exists. If `should_exist`
190             is set to false, will delete existing group instead of creating it.
191              
192             On undo, will delete Unix group previously created.
193              
194             On redo, will recreate the Unix group with the same GID.
195              
196             _
197             args => {
198             should_exist => {
199             summary => 'Whether group should exist',
200             schema => [bool => {default=>1}],
201             },
202             should_already_exist => {
203             summary => 'Whether group should already exist',
204             schema => 'bool',
205             },
206             %{ $SPEC{addgroup}{args} },
207             },
208             features => {
209             tx => {v=>2},
210             idempotent => 1,
211             },
212             };
213             for (qw/setup_unix_group/) {
214             $SPEC{$_}{args}{min_new_gid} = delete $SPEC{$_}{args}{min_gid};
215             $SPEC{$_}{args}{max_new_gid} = delete $SPEC{$_}{args}{max_gid};
216             $SPEC{$_}{args}{new_gid} = delete $SPEC{$_}{args}{gid};
217             }
218             sub setup_unix_group {
219 56     56 1 2921671 my %args = @_;
220              
221             # TMP, schema
222 56         281 my $dry_run = $args{-dry_run};
223 56 50       328 my $group = $args{group} or return [400, "Please specify group"];
224 56 50       651 $group =~ $Unix::Passwd::File::re_group
225             or return [400, "Invalid group"];
226 56   100     300 my $should_exist = $args{should_exist} // 1;
227 56         201 my $should_aexist = $args{should_already_exist};
228 56         301 my %ca = (etc_dir=>$args{etc_dir}, group=>$group);
229              
230 56         484 my $exists = Unix::Passwd::File::group_exists(%ca);
231 56         38612 my (@do, @undo);
232              
233             #$log->tracef("group=%s, exists=%s, should_exist=%s, ", $group, $exists, $should_exist);
234 56 100       238 if ($exists) {
235 19 100       109 if (!$should_exist) {
236 12         137 log_info("(DRY) Deleting group $group ...");
237 12         130 push @do , [delgroup=>{%ca}];
238             unshift @undo, [addgroup=>{
239             %ca,
240             maybe gid => $args{new_gid},
241             maybe min_gid => $args{min_new_gid},
242             maybe max_gid => $args{max_new_gid},
243 12         215 }];
244             }
245             } else {
246 37 100       184 if ($should_aexist) {
    100          
247 2         34 return [412, "Group $group should already exist"];
248             } elsif ($should_exist) {
249 32         302 log_info("(DRY) Adding group $group ...");
250             push @do , [addgroup=>{
251             %ca,
252             maybe gid => $args{new_gid},
253             maybe min_gid => $args{min_new_gid},
254             maybe max_gid => $args{max_new_gid},
255 32         581 }];
256 32         200 unshift @do , [delgroup=>{%ca}];
257             }
258             }
259              
260 54 100       234 if (@do) {
261 44         1608 return [200, "", undef, {do_actions=>\@do, undo_actions=>\@undo}];
262             } else {
263 10         246 return [304, "Already fixed"];
264             }
265             }
266              
267             1;
268             # ABSTRACT: Setup Unix group (existence)
269              
270             __END__
271              
272             =pod
273              
274             =encoding UTF-8
275              
276             =head1 NAME
277              
278             Setup::Unix::Group - Setup Unix group (existence)
279              
280             =head1 VERSION
281              
282             This document describes version 0.14 of Setup::Unix::Group (from Perl distribution Setup-Unix-User), released on 2017-07-10.
283              
284             =head1 FUNCTIONS
285              
286              
287             =head2 addgroup
288              
289             Usage:
290              
291             addgroup(%args) -> [status, msg, result, meta]
292              
293             Add group.
294              
295             This function is not exported.
296              
297             This function is idempotent (repeated invocations with same arguments has the same effect as single invocation). This function supports transactions.
298              
299              
300             Arguments ('*' denotes required arguments):
301              
302             =over 4
303              
304             =item * B<etc_dir> => I<str> (default: "/etc")
305              
306             Location of passwd files.
307              
308             =item * B<gid> => I<int>
309              
310             Add with specified GID.
311              
312             If not specified, will search an unused GID from C<min_new_gid> to C<max_new_gid>.
313              
314             If specified, will accept non-unique GID (that which has been used by other
315             group).
316              
317             =item * B<group> => I<str>
318              
319             Group name.
320              
321             =item * B<max_gid> => I<int> (default: 65534)
322              
323             Specify range for new GID.
324              
325             If a free GID between C<min_gid> and C<max_gid> is not available, an error is
326             returned.
327              
328             Passed to Unix::Passwd::File's C<max_new_gid>.
329              
330             =item * B<min_gid> => I<int> (default: 1000)
331              
332             Specify range for new GID.
333              
334             If a free GID between C<min_gid> and C<max_gid> is not available, an error is
335             returned.
336              
337             Passed to Unix::Passwd::File's C<min_new_gid>.
338              
339             =back
340              
341             Special arguments:
342              
343             =over 4
344              
345             =item * B<-tx_action> => I<str>
346              
347             For more information on transaction, see L<Rinci::Transaction>.
348              
349             =item * B<-tx_action_id> => I<str>
350              
351             For more information on transaction, see L<Rinci::Transaction>.
352              
353             =item * B<-tx_recovery> => I<str>
354              
355             For more information on transaction, see L<Rinci::Transaction>.
356              
357             =item * B<-tx_rollback> => I<str>
358              
359             For more information on transaction, see L<Rinci::Transaction>.
360              
361             =item * B<-tx_v> => I<str>
362              
363             For more information on transaction, see L<Rinci::Transaction>.
364              
365             =back
366              
367             Returns an enveloped result (an array).
368              
369             First element (status) is an integer containing HTTP status code
370             (200 means OK, 4xx caller error, 5xx function error). Second element
371             (msg) is a string containing error message, or 'OK' if status is
372             200. Third element (result) is optional, the actual result. Fourth
373             element (meta) is called result metadata and is optional, a hash
374             that contains extra information.
375              
376             Return value: (any)
377              
378              
379             =head2 delgroup
380              
381             Usage:
382              
383             delgroup(%args) -> [status, msg, result, meta]
384              
385             Delete group.
386              
387             Fixed state: group does not exist.
388              
389             Fixable state: group exists.
390              
391             This function is not exported.
392              
393             This function is idempotent (repeated invocations with same arguments has the same effect as single invocation). This function supports transactions.
394              
395              
396             Arguments ('*' denotes required arguments):
397              
398             =over 4
399              
400             =item * B<etc_dir> => I<str> (default: "/etc")
401              
402             Location of passwd files.
403              
404             =item * B<group> => I<str>
405              
406             Group name.
407              
408             =back
409              
410             Special arguments:
411              
412             =over 4
413              
414             =item * B<-tx_action> => I<str>
415              
416             For more information on transaction, see L<Rinci::Transaction>.
417              
418             =item * B<-tx_action_id> => I<str>
419              
420             For more information on transaction, see L<Rinci::Transaction>.
421              
422             =item * B<-tx_recovery> => I<str>
423              
424             For more information on transaction, see L<Rinci::Transaction>.
425              
426             =item * B<-tx_rollback> => I<str>
427              
428             For more information on transaction, see L<Rinci::Transaction>.
429              
430             =item * B<-tx_v> => I<str>
431              
432             For more information on transaction, see L<Rinci::Transaction>.
433              
434             =back
435              
436             Returns an enveloped result (an array).
437              
438             First element (status) is an integer containing HTTP status code
439             (200 means OK, 4xx caller error, 5xx function error). Second element
440             (msg) is a string containing error message, or 'OK' if status is
441             200. Third element (result) is optional, the actual result. Fourth
442             element (meta) is called result metadata and is optional, a hash
443             that contains extra information.
444              
445             Return value: (any)
446              
447              
448             =head2 setup_unix_group
449              
450             Usage:
451              
452             setup_unix_group(%args) -> [status, msg, result, meta]
453              
454             Setup Unix group (existence).
455              
456             On do, will create Unix group if not already exists. The created GID will be
457             returned in the result (C<< {gid =E<gt> GID} >>). If C<should_already_exist> is set to
458             true, won't create but only require that group already exists. If C<should_exist>
459             is set to false, will delete existing group instead of creating it.
460              
461             On undo, will delete Unix group previously created.
462              
463             On redo, will recreate the Unix group with the same GID.
464              
465             This function is not exported by default, but exportable.
466              
467             This function is idempotent (repeated invocations with same arguments has the same effect as single invocation). This function supports transactions.
468              
469              
470             Arguments ('*' denotes required arguments):
471              
472             =over 4
473              
474             =item * B<etc_dir> => I<str> (default: "/etc")
475              
476             Location of passwd files.
477              
478             =item * B<group> => I<str>
479              
480             Group name.
481              
482             =item * B<max_new_gid> => I<int> (default: 65534)
483              
484             Specify range for new GID.
485              
486             If a free GID between C<min_gid> and C<max_gid> is not available, an error is
487             returned.
488              
489             Passed to Unix::Passwd::File's C<max_new_gid>.
490              
491             =item * B<min_new_gid> => I<int> (default: 1000)
492              
493             Specify range for new GID.
494              
495             If a free GID between C<min_gid> and C<max_gid> is not available, an error is
496             returned.
497              
498             Passed to Unix::Passwd::File's C<min_new_gid>.
499              
500             =item * B<new_gid> => I<int>
501              
502             Add with specified GID.
503              
504             If not specified, will search an unused GID from C<min_new_gid> to C<max_new_gid>.
505              
506             If specified, will accept non-unique GID (that which has been used by other
507             group).
508              
509             =item * B<should_already_exist> => I<bool>
510              
511             Whether group should already exist.
512              
513             =item * B<should_exist> => I<bool> (default: 1)
514              
515             Whether group should exist.
516              
517             =back
518              
519             Special arguments:
520              
521             =over 4
522              
523             =item * B<-tx_action> => I<str>
524              
525             For more information on transaction, see L<Rinci::Transaction>.
526              
527             =item * B<-tx_action_id> => I<str>
528              
529             For more information on transaction, see L<Rinci::Transaction>.
530              
531             =item * B<-tx_recovery> => I<str>
532              
533             For more information on transaction, see L<Rinci::Transaction>.
534              
535             =item * B<-tx_rollback> => I<str>
536              
537             For more information on transaction, see L<Rinci::Transaction>.
538              
539             =item * B<-tx_v> => I<str>
540              
541             For more information on transaction, see L<Rinci::Transaction>.
542              
543             =back
544              
545             Returns an enveloped result (an array).
546              
547             First element (status) is an integer containing HTTP status code
548             (200 means OK, 4xx caller error, 5xx function error). Second element
549             (msg) is a string containing error message, or 'OK' if status is
550             200. Third element (result) is optional, the actual result. Fourth
551             element (meta) is called result metadata and is optional, a hash
552             that contains extra information.
553              
554             Return value: (any)
555              
556             =head1 FAQ
557              
558             =head1 HOMEPAGE
559              
560             Please visit the project's homepage at L<https://metacpan.org/release/Setup-Unix-User>.
561              
562             =head1 SOURCE
563              
564             Source repository is at L<https://github.com/perlancar/perl-Setup-Unix-User>.
565              
566             =head1 BUGS
567              
568             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Setup-Unix-User>
569              
570             When submitting a bug or request, please include a test-file or a
571             patch to an existing test-file that illustrates the bug or desired
572             feature.
573              
574             =head1 SEE ALSO
575              
576             L<Setup>
577              
578             L<Setup::Unix::User>
579              
580             =head1 AUTHOR
581              
582             perlancar <perlancar@cpan.org>
583              
584             =head1 COPYRIGHT AND LICENSE
585              
586             This software is copyright (c) 2017, 2015, 2014, 2012, 2011 by perlancar@cpan.org.
587              
588             This is free software; you can redistribute it and/or modify it under
589             the same terms as the Perl 5 programming language system itself.
590              
591             =cut