automate-it 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.hgignore +10 -0
  4. data/.loadpath +5 -0
  5. data/.project +17 -0
  6. data/CHANGES.txt +314 -0
  7. data/Hoe.rake +40 -0
  8. data/Manifest.txt +164 -0
  9. data/README.txt +40 -0
  10. data/Rakefile +256 -0
  11. data/TESTING.txt +57 -0
  12. data/TODO.txt +50 -0
  13. data/TUTORIAL.txt +391 -0
  14. data/automate-it.gemspec +25 -0
  15. data/bin/ai +3 -0
  16. data/bin/aifield +75 -0
  17. data/bin/aissh +93 -0
  18. data/bin/aitag +134 -0
  19. data/bin/automateit +133 -0
  20. data/docs/friendly_errors.txt +50 -0
  21. data/docs/previews.txt +86 -0
  22. data/examples/basic/Rakefile +26 -0
  23. data/examples/basic/config/automateit_env.rb +16 -0
  24. data/examples/basic/config/fields.yml +3 -0
  25. data/examples/basic/config/tags.yml +7 -0
  26. data/examples/basic/dist/README.txt +9 -0
  27. data/examples/basic/dist/myapp_server.erb +30 -0
  28. data/examples/basic/install.log +15 -0
  29. data/examples/basic/lib/README.txt +10 -0
  30. data/examples/basic/recipes/README.txt +4 -0
  31. data/examples/basic/recipes/install.rb +61 -0
  32. data/examples/basic/recipes/uninstall.rb +6 -0
  33. data/gpl.txt +674 -0
  34. data/helpers/cpan_wrapper.pl +220 -0
  35. data/helpers/which.cmd +7 -0
  36. data/lib/automateit.rb +55 -0
  37. data/lib/automateit/account_manager.rb +114 -0
  38. data/lib/automateit/account_manager/base.rb +138 -0
  39. data/lib/automateit/account_manager/etc.rb +128 -0
  40. data/lib/automateit/account_manager/nscd.rb +33 -0
  41. data/lib/automateit/account_manager/passwd_expect.rb +40 -0
  42. data/lib/automateit/account_manager/passwd_pty.rb +69 -0
  43. data/lib/automateit/account_manager/posix.rb +138 -0
  44. data/lib/automateit/address_manager.rb +88 -0
  45. data/lib/automateit/address_manager/base.rb +171 -0
  46. data/lib/automateit/address_manager/bsd.rb +28 -0
  47. data/lib/automateit/address_manager/freebsd.rb +59 -0
  48. data/lib/automateit/address_manager/linux.rb +42 -0
  49. data/lib/automateit/address_manager/openbsd.rb +66 -0
  50. data/lib/automateit/address_manager/portable.rb +37 -0
  51. data/lib/automateit/address_manager/sunos.rb +34 -0
  52. data/lib/automateit/cli.rb +85 -0
  53. data/lib/automateit/common.rb +65 -0
  54. data/lib/automateit/constants.rb +35 -0
  55. data/lib/automateit/download_manager.rb +48 -0
  56. data/lib/automateit/edit_manager.rb +321 -0
  57. data/lib/automateit/error.rb +10 -0
  58. data/lib/automateit/field_manager.rb +103 -0
  59. data/lib/automateit/interpreter.rb +631 -0
  60. data/lib/automateit/package_manager.rb +257 -0
  61. data/lib/automateit/package_manager/apt.rb +27 -0
  62. data/lib/automateit/package_manager/cpan.rb +101 -0
  63. data/lib/automateit/package_manager/dpkg.rb +54 -0
  64. data/lib/automateit/package_manager/egg.rb +64 -0
  65. data/lib/automateit/package_manager/gem.rb +201 -0
  66. data/lib/automateit/package_manager/pear.rb +95 -0
  67. data/lib/automateit/package_manager/pecl.rb +80 -0
  68. data/lib/automateit/package_manager/portage.rb +69 -0
  69. data/lib/automateit/package_manager/yum.rb +65 -0
  70. data/lib/automateit/platform_manager.rb +49 -0
  71. data/lib/automateit/platform_manager/darwin.rb +30 -0
  72. data/lib/automateit/platform_manager/debian.rb +26 -0
  73. data/lib/automateit/platform_manager/freebsd.rb +29 -0
  74. data/lib/automateit/platform_manager/gentoo.rb +26 -0
  75. data/lib/automateit/platform_manager/lsb.rb +44 -0
  76. data/lib/automateit/platform_manager/openbsd.rb +28 -0
  77. data/lib/automateit/platform_manager/struct.rb +80 -0
  78. data/lib/automateit/platform_manager/sunos.rb +39 -0
  79. data/lib/automateit/platform_manager/uname.rb +29 -0
  80. data/lib/automateit/platform_manager/windows.rb +40 -0
  81. data/lib/automateit/plugin.rb +7 -0
  82. data/lib/automateit/plugin/base.rb +32 -0
  83. data/lib/automateit/plugin/driver.rb +256 -0
  84. data/lib/automateit/plugin/manager.rb +224 -0
  85. data/lib/automateit/project.rb +493 -0
  86. data/lib/automateit/root.rb +17 -0
  87. data/lib/automateit/service_manager.rb +93 -0
  88. data/lib/automateit/service_manager/chkconfig.rb +39 -0
  89. data/lib/automateit/service_manager/rc_update.rb +37 -0
  90. data/lib/automateit/service_manager/sysv.rb +139 -0
  91. data/lib/automateit/service_manager/update_rcd.rb +35 -0
  92. data/lib/automateit/shell_manager.rb +316 -0
  93. data/lib/automateit/shell_manager/base_link.rb +67 -0
  94. data/lib/automateit/shell_manager/link.rb +24 -0
  95. data/lib/automateit/shell_manager/portable.rb +523 -0
  96. data/lib/automateit/shell_manager/symlink.rb +32 -0
  97. data/lib/automateit/shell_manager/which_base.rb +30 -0
  98. data/lib/automateit/shell_manager/which_unix.rb +16 -0
  99. data/lib/automateit/shell_manager/which_windows.rb +20 -0
  100. data/lib/automateit/tag_manager.rb +127 -0
  101. data/lib/automateit/tag_manager/struct.rb +121 -0
  102. data/lib/automateit/tag_manager/tag_parser.rb +93 -0
  103. data/lib/automateit/tag_manager/yaml.rb +29 -0
  104. data/lib/automateit/template_manager.rb +56 -0
  105. data/lib/automateit/template_manager/base.rb +181 -0
  106. data/lib/automateit/template_manager/erb.rb +17 -0
  107. data/lib/ext/metaclass.rb +17 -0
  108. data/lib/ext/object.rb +18 -0
  109. data/lib/ext/shell_escape.rb +7 -0
  110. data/lib/hashcache.rb +22 -0
  111. data/lib/helpful_erb.rb +63 -0
  112. data/lib/inactive_support.rb +53 -0
  113. data/lib/inactive_support/basic_object.rb +6 -0
  114. data/lib/inactive_support/clean_logger.rb +127 -0
  115. data/lib/inactive_support/core_ext/array/extract_options.rb +19 -0
  116. data/lib/inactive_support/core_ext/blank.rb +50 -0
  117. data/lib/inactive_support/core_ext/class/attribute_accessors.rb +48 -0
  118. data/lib/inactive_support/core_ext/class/inheritable_attributes.rb +140 -0
  119. data/lib/inactive_support/core_ext/enumerable.rb +63 -0
  120. data/lib/inactive_support/core_ext/hash/keys.rb +54 -0
  121. data/lib/inactive_support/core_ext/module/aliasing.rb +70 -0
  122. data/lib/inactive_support/core_ext/numeric/time.rb +91 -0
  123. data/lib/inactive_support/core_ext/string/inflections.rb +153 -0
  124. data/lib/inactive_support/core_ext/symbol.rb +14 -0
  125. data/lib/inactive_support/core_ext/time/conversions.rb +96 -0
  126. data/lib/inactive_support/duration.rb +96 -0
  127. data/lib/inactive_support/inflections.rb +53 -0
  128. data/lib/inactive_support/inflector.rb +282 -0
  129. data/lib/nested_error.rb +33 -0
  130. data/lib/nitpick.rb +33 -0
  131. data/lib/queued_logger.rb +68 -0
  132. data/lib/tempster.rb +250 -0
  133. data/misc/index_gem_repository.rb +304 -0
  134. data/misc/setup_egg.rb +12 -0
  135. data/misc/setup_gem_dependencies.sh +6 -0
  136. data/misc/setup_rubygems.sh +21 -0
  137. metadata +279 -0
@@ -0,0 +1,220 @@
1
+ # This file can be used as both a Perl library (read the POD below) and a
2
+ # stand-alone program (run it with "--help" for instructions).
3
+
4
+ =head1 NAME
5
+
6
+ CpanWrapper - Provides a simpler wrapper for the CPAN package manager.
7
+
8
+ =head1 DESCRIPTION
9
+
10
+ This module provides easy-to-use methods for installing, uninstalling and
11
+ querying the status of CPAN modules.
12
+
13
+ =over
14
+
15
+ =cut
16
+
17
+ use warnings "all";
18
+
19
+ package CpanWrapper;
20
+
21
+ require CPAN;
22
+ require ExtUtils::Packlist;
23
+ require ExtUtils::Installed;
24
+ require Tie::Handle;
25
+
26
+ =head1 CLASS VARIABLES
27
+
28
+ =item $CpanWrapper::DRYRUN
29
+
30
+ Should actions really happen? E.g., in dry-run mode, the uninstall will only
31
+ pretend to delete files.
32
+
33
+ =cut
34
+
35
+ our $DRYRUN = 0;
36
+
37
+ =head1 CLASS METHODS
38
+
39
+ =item CpanWrapper->query($module_name)
40
+
41
+ Query the module and return 1 if it's installed, 0 if not.
42
+
43
+ =cut
44
+ sub query {
45
+ my($class, $module) = @_;
46
+ no warnings;
47
+ my $result = $CPAN::META->has_inst($module);
48
+ use warnings "all";
49
+ return $result;
50
+ }
51
+
52
+ =item CpanWrapper->uninstall($module_name)
53
+
54
+ Uninstall the module. Returns an array of files removed.
55
+
56
+ =cut
57
+ sub uninstall {
58
+ # /usr/local/lib/perl/5.8.8/auto/ack/.packlist
59
+ my($class, $module) = @_;
60
+ my $packlists = ExtUtils::Installed->new;
61
+ my @result;
62
+ foreach my $file ($packlists->files($module)) {
63
+ push(@result, $file);
64
+ unlink $file unless $DRYRUN;
65
+ }
66
+ my $packlist = $packlists->packlist($module)->packlist_file();
67
+ push(@result, $packlist);
68
+ unlink $packlist unless $DRYRUN;
69
+ return @result;
70
+ }
71
+
72
+ =item CpanWrapper->install($module_name)
73
+
74
+ Install the module. Returns 0 if can't find module.
75
+
76
+ =cut
77
+ sub install {
78
+ no warnings;
79
+ my($class, $module) = @_;
80
+ tie *NO, 'NoHandle';
81
+ open(SAVEIN, ">&STDIN");
82
+ open(STDIN, ">&NO"); # TODO why isn't this enough?
83
+ *STDIN = *NO;
84
+ my $result;
85
+ if (my $module_ref = CPAN::Shell->expand('Module', $module)) {
86
+ $module_ref->install unless $DRYRUN;
87
+ $result = 1;
88
+ } else {
89
+ $result = 0;
90
+ }
91
+ open(STDIN, ">&SAVEIN");
92
+ close NO;
93
+ use warnings "all";
94
+ return $result;
95
+ }
96
+
97
+ =item NoHandle
98
+
99
+ File handle that responds with "no" to all readline queries. This is used
100
+ during the install process to reject CPAN's unreasonable defaults.
101
+
102
+ =cut
103
+ package NoHandle;
104
+ sub TIEHANDLE { my $self; bless \$self, shift }
105
+ sub WRITE { die }
106
+ sub PRINT { die }
107
+ sub PRINTF { die }
108
+ sub READ { die }
109
+ sub READLINE { print "no\n"; return "no\n" }
110
+ sub GETC { die }
111
+ sub CLOSE { }
112
+ sub OPEN { }
113
+ sub BINMODE { }
114
+ sub EOF { 0 }
115
+ sub TELL { }
116
+ sub DESTROY { }
117
+
118
+ #===[ command-line usage ]==============================================
119
+
120
+ package main;
121
+ if ($0 eq __FILE__) {
122
+ sub usage {
123
+ my($message) = @_;
124
+ print <<HERE;
125
+ USAGE: cpan_wrapper.pl [OPTIONS] ACTION module [modules...]
126
+
127
+ OPTIONS:
128
+ --help
129
+ Show this help
130
+ --quiet
131
+ Don't print anything other than what CPAN generates
132
+ --dryrun
133
+ Don't actually perform actions, just pretend to
134
+
135
+ ACTIONS:
136
+
137
+ --install
138
+ Install modules
139
+ --uninstall
140
+ Uninstall modules
141
+ --query
142
+ Display which packages are installed and which aren't
143
+ HERE
144
+
145
+ if ($message) {
146
+ print "ERROR: $message\n";
147
+ exit 1;
148
+ } else {
149
+ exit 0;
150
+ }
151
+ }
152
+
153
+ use Getopt::Long;
154
+ our $quiet = 0;
155
+ our $dryrun = 0;
156
+ our $help = 0;
157
+ our $install = 0;
158
+ our $uninstall = 0;
159
+ our $query = 0;
160
+ GetOptions(
161
+ 'quiet' => \$quiet,
162
+ 'dryrun' => \$dryrun,
163
+ 'n' => \$dryrun,
164
+ 'help' => \$help,
165
+ 'install' => \$install,
166
+ 'uninstall' => \$uninstall,
167
+ 'query' => \$query,
168
+ 'q' => \$query
169
+ );
170
+ my @modules = @ARGV;
171
+
172
+ usage(0) if 1 == $help;
173
+ usage("No action specified") unless $install or $uninstall or $query;
174
+ usage("No modules specified") unless $#modules >= 0;
175
+
176
+ if ($install) {
177
+ foreach my $module (@modules) {
178
+ if (CpanWrapper->install($module)) {
179
+ print "* Installed: $module\n" unless $quiet;
180
+ } else {
181
+ print "! Can't find CPAN module: $module\n";
182
+ exit 1
183
+ }
184
+ }
185
+ } elsif ($uninstall) {
186
+ foreach my $module (@modules) {
187
+ print "* Uninstalling module: $module\n" unless $quiet;
188
+
189
+ my(@files) = CpanWrapper->uninstall($module);
190
+ foreach my $file (@files) {
191
+ print "- $file\n" unless $quiet;
192
+ }
193
+ }
194
+ } elsif ($query) {
195
+ my @available;
196
+ my @unavailable;
197
+ foreach my $module (@modules) {
198
+ if (CpanWrapper->query($module)) {
199
+ push(@available, $module);
200
+ } else {
201
+ push(@unavailable, $module);
202
+ }
203
+ }
204
+
205
+ sub print_contents {
206
+ my($name, @modules) = @_;
207
+ return if $#modules < 0;
208
+ print "$name:\n";
209
+ foreach my $module (@modules) {
210
+ print " - $module\n";
211
+ }
212
+ }
213
+
214
+ print "--- %YAML:1.0\n";
215
+ print_contents 'available', @available;
216
+ print_contents 'unavailable', @unavailable;
217
+ }
218
+ }
219
+
220
+ 1;
data/helpers/which.cmd ADDED
@@ -0,0 +1,7 @@
1
+ @ rem "which" workalike for Windows. Searches current directory and PATH for all extensions, stops after first match, and returns meaningful exit value for "system" or "ERRORLEVEL" checks.
2
+ @setlocal
3
+ @set P2=.;%PATH%
4
+ @for %%e in (%PATHEXT%) do @for %%i in (%1%%e) do @if NOT "%%~$P2:i"=="" echo %%~$P2:i && goto end
5
+ @rem REM This exits the shell on failure, so don't use it directly
6
+ @exit 1
7
+ :end
data/lib/automateit.rb ADDED
@@ -0,0 +1,55 @@
1
+ # == Dependencies
2
+
3
+ # Standard libraries
4
+ require 'etc'
5
+ require 'fileutils'
6
+ require 'find'
7
+ require 'logger'
8
+ require 'open3'
9
+ require 'pp'
10
+ require 'pathname'
11
+ require 'resolv'
12
+ require 'set'
13
+ require 'socket'
14
+ require 'yaml'
15
+
16
+ # Gems
17
+ require 'rubygems'
18
+ require 'erb'
19
+
20
+ # Extensions
21
+ require 'inactive_support'
22
+ require 'ext/object.rb'
23
+ require 'ext/metaclass.rb'
24
+ require 'ext/shell_escape.rb'
25
+
26
+ # Helpers
27
+ require 'hashcache'
28
+ require 'queued_logger'
29
+ require 'tempster'
30
+ require 'helpful_erb'
31
+ require 'nested_error'
32
+ require 'nitpick'
33
+
34
+ # Core
35
+ require 'automateit/root'
36
+ require 'automateit/constants'
37
+ require 'automateit/error'
38
+ require 'automateit/common'
39
+ require 'automateit/interpreter'
40
+ require 'automateit/plugin'
41
+ require 'automateit/cli'
42
+ require 'automateit/project'
43
+
44
+ # Plugins which must be loaded early
45
+ require 'automateit/shell_manager'
46
+ require 'automateit/platform_manager' # requires shell
47
+ require 'automateit/address_manager' # requires shell
48
+ require 'automateit/tag_manager' # requires address, platform
49
+ require 'automateit/field_manager' # requires shell
50
+ require 'automateit/service_manager' # requires shell
51
+ require 'automateit/package_manager' # requires shell
52
+ require 'automateit/template_manager'
53
+ require 'automateit/edit_manager'
54
+ require 'automateit/account_manager'
55
+ require 'automateit/download_manager'
@@ -0,0 +1,114 @@
1
+ # == AccountManager
2
+ #
3
+ # The AccountManager provides a way of managing system accounts, such as Unix
4
+ # users and groups.
5
+ class AutomateIt::AccountManager < AutomateIt::Plugin::Manager
6
+ # Invalidate system cache for +database+. The +database+ can be either :users
7
+ # or :groups. This is necessary on operating systems that lack logic to
8
+ # notify their caching system that an entry changed. If the OS doesn't need
9
+ # invalidation, will do nothing and return false.
10
+ #
11
+ # This method is primarily for the sake of driver authors, recipe authors
12
+ # will probably never need to use this.
13
+ def invalidate(database) dispatch_safely(database) end
14
+
15
+ #-----------------------------------------------------------------------
16
+
17
+ # Find a user account. Method returns a query helper which takes a
18
+ # +username+ as an index argument and returns a Struct::Passwd entry as
19
+ # described in Etc::getpwent if the user exists or a nil if not.
20
+ #
21
+ # Example:
22
+ # users["root"] # => #<struct Struct::Passwd name="root"...
23
+ #
24
+ # users["does_not_exist"] # => nil
25
+ def users() dispatch() end
26
+
27
+ # Add the +username+ if not already created.
28
+ #
29
+ # Options:
30
+ # * :description -- User's full name. Defaults to username.
31
+ # * :home -- Path to user's home directory. If not specified, uses system
32
+ # default like "/home/username".
33
+ # * :create_home -- Create homedir. Defaults to true.
34
+ # * :groups -- Array of group names to add this user to.
35
+ # * :shell -- Path to login shell. If not specified, uses system default
36
+ # like "/bin/bash".
37
+ # * :uid -- Fixnum user ID for user. Default chooses an unused id.
38
+ # * :gid -- Fixnum group ID for user. Default chooses same gid as uid.
39
+ #
40
+ # Example:
41
+ # add_user("bob", :description => "Bob Smith")
42
+ def add_user(username, opts={}) dispatch(username, opts) end
43
+
44
+ # TODO AccountManager#update_user -- implement
45
+ ### def update_user(username, opts={}) dispatch(username, opts) end
46
+
47
+ # Remove the +username+ if present.
48
+ #
49
+ # Options:
50
+ # * :remove_home -- Delete user's home directory and mail spool. Default is
51
+ # true.
52
+ def remove_user(username, opts={}) dispatch(username, opts) end
53
+
54
+ # Is +user+ present?
55
+ def has_user?(user) dispatch(user) end
56
+
57
+ # Add +groups+ (array of groupnames) to +user+.
58
+ def add_groups_to_user(groups, user) dispatch(groups, user) end
59
+
60
+ # Remove +groups+ (array of groupnames) from +user+.
61
+ def remove_groups_from_user(groups, user) dispatch(groups, user) end
62
+
63
+ # Change the +password+ for the +user+.
64
+ def passwd(user, password, opts={}) dispatch(user, password, opts) end
65
+
66
+ #.......................................................................
67
+
68
+ # Find a group. Method returns a query helper which takes a
69
+ # +groupname+ as an index argument and returns a Struct::Group entry as
70
+ # described in Etc::getgrent if the group exists or a nil if not.
71
+ #
72
+ # Example:
73
+ # groups["root"] # => #<struct Struct::Group name="root"...
74
+ #
75
+ # groups["does_not_exist"] # => nil
76
+ def groups() dispatch() end
77
+
78
+ # Add +groupname+ if it doesn't exist. Options:
79
+ # * :members -- Array of usernames to add as members.
80
+ # * :gid -- Group ID to use. Default is to find an unused id.
81
+ def add_group(groupname, opts={}) dispatch(groupname, opts) end
82
+
83
+ # TODO AccountManager#update_group -- implement
84
+ ### def update_group(groupname, opts={}) dispatch(groupname, opts) end
85
+
86
+ # Remove +groupname+ if it exists.
87
+ def remove_group(groupname, opts={}) dispatch(groupname, opts) end
88
+
89
+ # Does +group+ exist?
90
+ def has_group?(group) dispatch(group) end
91
+
92
+ # Add +users+ (array of usernames) to +group+.
93
+ def add_users_to_group(users, group) dispatch(users, group) end
94
+
95
+ # Remove +users+ (array of usernames) from +group+.
96
+ def remove_users_from_group(users, group) dispatch(users, group) end
97
+
98
+ # Array of groupnames this user is a member of.
99
+ def groups_for_user(query) dispatch(query) end
100
+
101
+ # Array of usernames in group.
102
+ def users_for_group(query) dispatch(query) end
103
+
104
+ # Hash of usernames and the groupnames they're members of.
105
+ def users_to_groups() dispatch() end
106
+ end # class AccountManager
107
+
108
+ # Drivers
109
+ require 'automateit/account_manager/base'
110
+ require 'automateit/account_manager/passwd_pty'
111
+ require 'automateit/account_manager/passwd_expect'
112
+ require 'automateit/account_manager/nscd'
113
+ require 'automateit/account_manager/etc'
114
+ require 'automateit/account_manager/posix'
@@ -0,0 +1,138 @@
1
+ # == AccountManager::BaseDriver
2
+ #
3
+ # Base class for all AccountManager drivers.
4
+ class ::AutomateIt::AccountManager::BaseDriver < ::AutomateIt::Plugin::Driver
5
+ protected
6
+
7
+ def _passwd_helper(user, password, opts={}, &block)
8
+ users = manager.users
9
+
10
+ unless users[user]
11
+ if preview?
12
+ log.info(PNOTE+"Setting password for user: #{user}")
13
+ return true
14
+ else
15
+ raise ArgumentError.new("No such user: #{user}")
16
+ end
17
+ end
18
+
19
+ case user
20
+ when Symbol
21
+ user = user.to_s
22
+ when Integer
23
+ user = users[user]
24
+ when String
25
+ # leave it alone
26
+ else
27
+ raise TypeError.new("Unknown user type: #{user.class}")
28
+ end
29
+
30
+ return block.call(user, password, opts)
31
+ end
32
+
33
+ def _add_user_helper(username, opts={}, &block)
34
+ return false if has_user?(username)
35
+
36
+ # Create group first, then the user. Necessary because some OSes can't add users with non-existent groups.
37
+
38
+ if opts[:personal_group].nil? and not opts[:group] and not opts[:gid]
39
+ opts[:personal_group] = true
40
+ end
41
+
42
+ # FIXME how to default personal_group to false?
43
+ # FIXME what if want to add user to a specific group, rather than creating?
44
+ # FIXME what if want or not want to crease user group?
45
+ # FIXME how to find an unused gid/uid combo?
46
+
47
+ unless opts[:group] == false
48
+ groupname = opts[:group] || username
49
+ unless has_group?(groupname)
50
+ if not opts[:uid] and not opts[:gid] and group = add_group(groupname, opts)
51
+ opts[:uid] = opts[:gid] = group.gid
52
+ end
53
+ end
54
+ end
55
+
56
+ block.call(username, opts)
57
+ manager.invalidate(:passwd)
58
+
59
+ passwd_opts = {:quiet => opts[:quiet]}
60
+ manager.passwd(username, opts[:passwd], passwd_opts) if opts[:passwd]
61
+
62
+ manager.invalidate(:passwd)
63
+ return users[username]
64
+ end
65
+
66
+ def _remove_user_helper(username, opts={}, &block)
67
+ return false unless has_user?(username)
68
+
69
+ block.call(username, opts)
70
+ manager.invalidate(:passwd)
71
+ remove_group(username) if has_group?(username)
72
+
73
+ return true
74
+ end
75
+
76
+ def _add_groups_to_user_helper(groups, username, &block)
77
+ groups = [groups].flatten
78
+ present = groups_for_user(username)
79
+ missing = groups - present
80
+ return false if missing.empty?
81
+
82
+ block.call(missing, username)
83
+ manager.invalidate(:group)
84
+
85
+ return missing
86
+ end
87
+
88
+ def _remove_groups_from_user_helper(groups, username, &block)
89
+ groups = [groups].flatten
90
+ matched = groups_for_user(username)
91
+ present = groups & matched
92
+ return false if present.empty?
93
+
94
+ block.call(present, username)
95
+ manager.invalidate(:group)
96
+
97
+ return present
98
+ end
99
+
100
+ def _add_users_to_group_helper(users, groupname, &block)
101
+ users = [users].flatten
102
+ # XXX Include pwent.gid?
103
+ grent = groups[groupname]
104
+ missing = \
105
+ if writing? and not grent
106
+ raise ArgumentError.new("no such group: #{groupname}")
107
+ elsif writing? or grent
108
+ users - grent.mem
109
+ else
110
+ users
111
+ end
112
+ return false if missing.empty?
113
+
114
+ block.call(missing, groupname)
115
+ manager.invalidate(:groups)
116
+
117
+ return missing
118
+ end
119
+
120
+ def _remove_users_from_group_helper(users, groupname, &block)
121
+ users = [users].flatten
122
+ grent = groups[groupname]
123
+ present = \
124
+ if writing? and not grent
125
+ raise ArgumentError.new("no such group: #{groupname}")
126
+ elsif writing? or grent
127
+ grent.mem & users
128
+ else
129
+ users
130
+ end
131
+ return false if present.empty?
132
+
133
+ block.call(present, groupname)
134
+ manager.invalidate(:groups)
135
+
136
+ return present
137
+ end
138
+ end