automateit 0.71021 → 0.71030
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/CHANGES.txt +10 -0
- data/Hoe.rake +5 -3
- data/README.txt +1 -1
- data/Rakefile +12 -2
- data/TODO.txt +14 -9
- data/TUTORIAL.txt +1 -1
- data/lib/automateit.rb.orig +65 -0
- data/lib/automateit/account_manager.rb +18 -10
- data/lib/automateit/account_manager/base.rb +133 -0
- data/lib/automateit/account_manager/{portable.rb → etc.rb} +9 -8
- data/lib/automateit/account_manager/nscd.rb +28 -0
- data/lib/automateit/account_manager/passwd_expect.rb +40 -0
- data/lib/automateit/account_manager/passwd_pty.rb +67 -0
- data/lib/automateit/account_manager/posix.rb +126 -0
- data/lib/automateit/project.rb +43 -10
- data/lib/automateit/root.rb +1 -1
- data/spec/integration/account_manager_spec.rb +128 -43
- metadata +14 -10
- metadata.gz.sig +0 -0
- data/lib/automateit/account_manager/linux.rb +0 -173
- data/lib/automateit/account_manager/passwd.rb +0 -70
@@ -0,0 +1,40 @@
|
|
1
|
+
# == AccountManager::PasswdExpect
|
2
|
+
#
|
3
|
+
# An AccountManager driver for the +passwd+ command found on Unix-like systems
|
4
|
+
# using the +expect+ program as a wrapper because the Ruby PTY implementation
|
5
|
+
# is unreliable.
|
6
|
+
class ::AutomateIt::AccountManager::PasswdExpect < ::AutomateIt::AccountManager::BaseDriver
|
7
|
+
depends_on :programs => %w(passwd expect)
|
8
|
+
|
9
|
+
def suitability(method, *args) # :nodoc:
|
10
|
+
# Level must be higher than PasswdPTY
|
11
|
+
return available? ? 9 : 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# See AccountManager#passwd
|
15
|
+
def passwd(user, password, opts={})
|
16
|
+
_passwd_helper(user, password, opts) do |user, password, opts|
|
17
|
+
log.silence(Logger::WARN) do
|
18
|
+
interpreter.mktemp do |filename|
|
19
|
+
# Script derived from /usr/share/doc/expect/examples/autopasswd
|
20
|
+
interpreter.render(:text => <<-HERE, :to => filename)
|
21
|
+
set password "#{password}"
|
22
|
+
spawn passwd "#{user}"
|
23
|
+
expect "assword:"
|
24
|
+
sleep 0.1
|
25
|
+
send "$password\\r"
|
26
|
+
expect "assword:"
|
27
|
+
sleep 0.1
|
28
|
+
send "$password\\r"
|
29
|
+
expect eof
|
30
|
+
HERE
|
31
|
+
|
32
|
+
cmd = "expect #{filename} #{user} #{password}"
|
33
|
+
cmd << " > /dev/null" if opts[:quiet]
|
34
|
+
return(interpreter.sh cmd)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# == AccountManager::PasswdPTY
|
2
|
+
#
|
3
|
+
# An AccountManager driver for +passwd+ command found on Unix-like systems
|
4
|
+
# using the Ruby PTY implementation.
|
5
|
+
#
|
6
|
+
# *WARNING*: The Ruby PTY module is unreliable or unavailable on most
|
7
|
+
# platforms. It may hang indefinitely or report incorrect results. Every
|
8
|
+
# attempt has been made to work around these problems, but this is a low-level
|
9
|
+
# problem. You are strongly encouraged to install the +expect+ program, which
|
10
|
+
# works flawlessly. Once the +expect+ program is installed, passwords will be
|
11
|
+
# changed using the AccountManager::PasswdExpect driver, which works properly.
|
12
|
+
class ::AutomateIt::AccountManager::PasswdPTY < ::AutomateIt::AccountManager::BaseDriver
|
13
|
+
depends_on \
|
14
|
+
:programs => %w(passwd),
|
15
|
+
:libraries => %w(open3 expect pty)
|
16
|
+
|
17
|
+
def suitability(method, *args) # :nodoc:
|
18
|
+
# Level must be higher than Linux
|
19
|
+
return available? ? 3 : 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# See AccountManager#passwd
|
23
|
+
def passwd(user, password, opts={})
|
24
|
+
log.info(PERROR+"Setting password with flaky Ruby PTY, which hangs or fails randomly. Install 'expect' (http://expect.nist.gov/) for reliable operation.")
|
25
|
+
_passwd_helper(user, password, opts) do
|
26
|
+
log.silence(Logger::WARN) do
|
27
|
+
interpreter.mktemp do |filename|
|
28
|
+
tries = 5
|
29
|
+
exitstatus = nil
|
30
|
+
begin
|
31
|
+
exitstruct = _passwd_raw(user, password, opts)
|
32
|
+
if exitstatus and not exitstruct.exitstatus.zero?
|
33
|
+
# FIXME AccountManager::Linux#passwd -- The `passwd` command randomly returns exit status 10 even when it succeeds. What does this mean and how to deal with it?! Temporary workaround is to throw an error and force a retry.
|
34
|
+
raise Errno::EPIPE.new("bad exitstatus %s" % exitstruct.exitstatus)
|
35
|
+
end
|
36
|
+
rescue Errno::EPIPE => e
|
37
|
+
# FIXME AccountManager::Linux#passwd -- EPIPE exception randomly thrown even when `passwd` succeeds. How to eliminate it? How to differentiate between this false error and a real one?
|
38
|
+
if tries <= 0
|
39
|
+
raise e
|
40
|
+
else
|
41
|
+
tries -= 1
|
42
|
+
retry
|
43
|
+
end
|
44
|
+
end
|
45
|
+
return exitstruct.exitstatus.zero?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
def _passwd_raw(user, password, opts={})
|
53
|
+
quiet = (opts[:quiet] or not log.info?)
|
54
|
+
|
55
|
+
require 'open4'
|
56
|
+
return Open4::popen4("passwd %s 2>&1" % user) do |pid, sin, sout, serr|
|
57
|
+
$expect_verbose = ! quiet
|
58
|
+
2.times do
|
59
|
+
sout.expect(/:/)
|
60
|
+
sleep 0.1 # Reduce chance of passwd thinking we're a robot :(
|
61
|
+
sin.puts password
|
62
|
+
puts "*" * 12 unless quiet
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
protected :_passwd_raw
|
67
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# == AccountManager::POSIX
|
2
|
+
#
|
3
|
+
# A POSIX driver for the AccountManager.
|
4
|
+
class ::AutomateIt::AccountManager::POSIX < ::AutomateIt::AccountManager::Etc
|
5
|
+
depends_on :programs => %w(useradd usermod userdel groupadd groupmod groupdel)
|
6
|
+
|
7
|
+
def suitability(method, *args) # :nodoc:
|
8
|
+
# Level must be higher than Portable
|
9
|
+
return available? ? 2 : 0
|
10
|
+
end
|
11
|
+
|
12
|
+
#.......................................................................
|
13
|
+
|
14
|
+
# See AccountManager#add_user
|
15
|
+
def add_user(username, opts={})
|
16
|
+
return _add_user_helper(username, opts) do |username, opts|
|
17
|
+
cmd = "useradd"
|
18
|
+
cmd << " -c #{opts[:description] || username}"
|
19
|
+
cmd << " -d #{opts[:home]}" if opts[:home]
|
20
|
+
cmd << " -m" unless opts[:create_home] == false
|
21
|
+
cmd << " -G #{opts[:groups].join(',')}" if opts[:groups]
|
22
|
+
cmd << " -s #{opts[:shell] || "/bin/bash"}"
|
23
|
+
cmd << " -u #{opts[:uid]}" if opts[:uid]
|
24
|
+
cmd << " -g #{opts[:gid]}" if opts[:gid]
|
25
|
+
cmd << " #{username} < /dev/null"
|
26
|
+
cmd << " > /dev/null 2>&1 | grep -v blocks" if opts[:quiet]
|
27
|
+
interpreter.sh(cmd)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO AccountManager#update_user -- implement
|
32
|
+
### def update_user(username, opts={}) dispatch(username, opts) end
|
33
|
+
|
34
|
+
# See AccountManager#remove_user
|
35
|
+
def remove_user(username, opts={})
|
36
|
+
return _remove_user_helper(username, opts) do |username, opts|
|
37
|
+
# Options: -r -- remove the home directory and mail spool
|
38
|
+
cmd = "userdel"
|
39
|
+
cmd << " -r" unless opts[:remove_home] == false
|
40
|
+
cmd << " #{username}"
|
41
|
+
cmd << " > /dev/null" if opts[:quiet]
|
42
|
+
interpreter.sh(cmd)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# See AccountManager#add_groups_to_user
|
47
|
+
def add_groups_to_user(groups, username)
|
48
|
+
return _add_groups_to_user_helper(groups, username) do |missing, username|
|
49
|
+
targets = (groups_for_user(username) + missing).uniq
|
50
|
+
|
51
|
+
cmd = "usermod -G #{targets.join(',')} #{username}"
|
52
|
+
interpreter.sh(cmd)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# See AccountManager#remove_groups_from_user
|
57
|
+
def remove_groups_from_user(groups, username)
|
58
|
+
return _remove_groups_from_user_helper(groups, username) do |present, username|
|
59
|
+
matches = (groups_for_user(username) - [groups].flatten).uniq
|
60
|
+
cmd = "usermod -G #{matches.join(',')} #{username}"
|
61
|
+
interpreter.sh(cmd)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
#.......................................................................
|
66
|
+
|
67
|
+
# See AccountManager#add_group
|
68
|
+
def add_group(groupname, opts={})
|
69
|
+
modified = false
|
70
|
+
unless has_group?(groupname)
|
71
|
+
modified = true
|
72
|
+
|
73
|
+
cmd = "groupadd"
|
74
|
+
cmd << " -g #{opts[:gid]}" if opts[:gid]
|
75
|
+
cmd << " #{groupname}"
|
76
|
+
interpreter.sh(cmd)
|
77
|
+
|
78
|
+
manager.invalidate(:groups)
|
79
|
+
end
|
80
|
+
|
81
|
+
if opts[:members]
|
82
|
+
modified = true
|
83
|
+
add_users_to_group(opts[:members], groupname)
|
84
|
+
end
|
85
|
+
|
86
|
+
return modified ? groups[groupname] : false
|
87
|
+
end
|
88
|
+
|
89
|
+
# TODO AccountManager#update_group -- implement
|
90
|
+
### def update_group(groupname, opts={}) dispatch(groupname, opts) end
|
91
|
+
|
92
|
+
# See AccountManager#remove_group
|
93
|
+
def remove_group(groupname, opts={})
|
94
|
+
return false unless has_group?(groupname)
|
95
|
+
cmd = "groupdel #{groupname}"
|
96
|
+
interpreter.sh(cmd)
|
97
|
+
|
98
|
+
manager.invalidate(:groups)
|
99
|
+
|
100
|
+
return true
|
101
|
+
end
|
102
|
+
|
103
|
+
# See AccountManager#add_users_to_group
|
104
|
+
def add_users_to_group(users, groupname)
|
105
|
+
_add_users_to_group_helper(users, groupname) do |missing, groupname|
|
106
|
+
for username in missing
|
107
|
+
targets = (groups_for_user(username) + [groupname]).uniq
|
108
|
+
cmd = "usermod -G #{targets.join(',')} #{username}"
|
109
|
+
interpreter.sh(cmd)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# See AccountManager#remove_users_from_group
|
115
|
+
def remove_users_from_group(users, groupname)
|
116
|
+
_remove_users_from_group_helper(users, groupname) do |present, groupname|
|
117
|
+
u2g = users_to_groups
|
118
|
+
for username in present
|
119
|
+
user_groups = u2g[username]
|
120
|
+
# FIXME tries to include non-present groups, should use some variant of present
|
121
|
+
cmd = "usermod -G #{(user_groups.to_a-[groupname]).join(',')} #{username}"
|
122
|
+
interpreter.sh(cmd)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
data/lib/automateit/project.rb
CHANGED
@@ -51,6 +51,37 @@ module AutomateIt
|
|
51
51
|
#
|
52
52
|
# Hello world!
|
53
53
|
#
|
54
|
+
# === Partitioning recipes
|
55
|
+
#
|
56
|
+
# You should split up your recipe code into different recipe files. This will
|
57
|
+
# improve the clarity of your code because each file can perform one task,
|
58
|
+
# and you'll also be able to easily execute a specific recipe.
|
59
|
+
#
|
60
|
+
# For example, you can use a task-specific <tt>recipes/postgresql.rb</tt> to
|
61
|
+
# set up the PostgreSQL database server, and a <tt>recipes/apache.rb</tt> to
|
62
|
+
# setup the Apache web server.
|
63
|
+
#
|
64
|
+
# === Running recipes from other recipes
|
65
|
+
#
|
66
|
+
# You can run one recipe from another. It's a good idea to create a top-level
|
67
|
+
# recipe that invokes the other recipes. This lets you run a single recipe
|
68
|
+
# that will in turn run all your other recipes in the correct order, such as
|
69
|
+
# setting up the database server before the web server so that websites.
|
70
|
+
#
|
71
|
+
# For example, consider a <tt>recipes/all.rb</tt> file with these lines:
|
72
|
+
#
|
73
|
+
# invoke 'postgresql' if tagged? :postgresql_server
|
74
|
+
# invoke 'nginx' if tagged? :nginx_server
|
75
|
+
# invoke 'apache' if tagged? :apache_server
|
76
|
+
#
|
77
|
+
# The first line above checks to see if the current host has the
|
78
|
+
# <tt>postgresql_server</tt> tag, and if it does, invokes the
|
79
|
+
# <tt>recipes/postgresql.rb</tt> recipe.
|
80
|
+
#
|
81
|
+
# You must run recipes from other recipes using AutomateIt's +invoke+ method
|
82
|
+
# and not Ruby's +require+, because the +invoke+ passes along the AutomateIt
|
83
|
+
# interpreter to the other recipes so they can continue execution.
|
84
|
+
#
|
54
85
|
# === Using project libraries
|
55
86
|
#
|
56
87
|
# Any files ending with <tt>.rb</tt> that you put into the project's
|
@@ -133,8 +164,10 @@ module AutomateIt
|
|
133
164
|
# aifield -p /tmp/hello_project greeting
|
134
165
|
#
|
135
166
|
# The <tt>-p</tt> specifies the project path (its an alias for
|
136
|
-
# <tt>--project</tt>).
|
137
|
-
#
|
167
|
+
# <tt>--project</tt>).
|
168
|
+
#
|
169
|
+
# More commands are available. For documentation and examples run the
|
170
|
+
# following commands from the Unix shell:
|
138
171
|
#
|
139
172
|
# aifield --help
|
140
173
|
# aitag --help
|
@@ -151,21 +184,21 @@ module AutomateIt
|
|
151
184
|
# If you want to share a project between different hosts, you're responsible for distributing the files between them. This isn't a big deal though because these are just text files and your OS has dozens of excellent ways to distribute these.
|
152
185
|
#
|
153
186
|
# Common approaches to distribution:
|
154
|
-
# *
|
155
|
-
# *
|
156
|
-
# *
|
187
|
+
# * <b>Shared directory</b>: Your hosts mount a shared network directory (e.g., +nfs+ or +smb+) with your project. This is very easy if your hosts already have a shared directory, but can be a nuisance otherwise because it opens potential security holes and risks having you hosts hang if the master goes offline.
|
188
|
+
# * <b>Client pull</b>: Your hosts download the latest copy of your project from a master repository using a remote copy tool (e.g., +rsync+) or a revision control system (e.g., +cvs+, +svn+, +hg+). This is a safe, simple and secure option.
|
189
|
+
# * <b>Server push</b>: You have a master push out the project files to clients using a remote copy tool and then invoke +automateit+ on them via SSH. This can be awkward and time-consuming because the server must go through a list of all hosts and copy files to them individually.
|
157
190
|
#
|
158
191
|
# An example of a complete solution for distributing system configuration management files:
|
159
|
-
#
|
160
|
-
#
|
192
|
+
# 1. Setup an +svn+ or other version control repository to store your project and create a special account for the hosts to use to checkout code.
|
193
|
+
# 2. Write a wrapper script for running the recipes, for example, write a "/usr/bin/myautomateit" shell script like:
|
161
194
|
#
|
162
195
|
# #!/bin/sh
|
163
196
|
# cd /var/local/myautomateit
|
164
197
|
# svn update --quiet
|
165
198
|
# automateit recipe/default.rb
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
199
|
+
# 3. Run this wrapper once an hour using cron so that your systems are always up to date. AutomateIt only prints output when it makes a change, so cron will only email you when you commit new code to the repository and the hosts make changes.
|
200
|
+
# 4. If you need to run a recipe on the machine right now, SSH into it and run the wrapper.
|
201
|
+
# 5. If you need to run the script early on a bunch of machines and don't want to manually SSH into each one, you can leverage the +aitag+ (see <tt>aitag --help</tt>) to execute a Unix command across multiple systems. For example, you could use a Unix shell command like this to execute the wrapper on all hosts tagged with +apache_servers+:
|
169
202
|
#
|
170
203
|
# for host in `aitag -p /var/local/myautomateit -w apache_server`; do
|
171
204
|
# echo "# $host"
|
data/lib/automateit/root.rb
CHANGED
@@ -7,22 +7,65 @@ elsif not INTERPRETER.superuser?
|
|
7
7
|
elsif not INTERPRETER.account_manager.available?(:add_user)
|
8
8
|
puts "NOTE: Can't find AccountManager for this platform, #{__FILE__}"
|
9
9
|
else
|
10
|
-
describe
|
10
|
+
describe AutomateIt::AccountManager do
|
11
11
|
before(:all) do
|
12
|
+
### @independent = true
|
13
|
+
@independent = false
|
14
|
+
|
15
|
+
### @a = AutomateIt.new(:verbosity => Logger::INFO)
|
12
16
|
@a = AutomateIt.new(:verbosity => Logger::WARN)
|
13
17
|
@m = @a.account_manager
|
18
|
+
@quiet = ! @a.log.info?
|
14
19
|
|
15
|
-
|
16
|
-
@
|
20
|
+
# Some OSes are limited to 8 character names :(
|
21
|
+
@username = "aitestus"
|
22
|
+
@groupname = "aitestgr"
|
17
23
|
|
18
|
-
|
19
|
-
|
24
|
+
begin
|
25
|
+
raise "User named '#{@username}' found. If this isn't a real user, delete it so that the test can contineu. If this is a real user, change the spec to test with a user that shouldn't exist." if @m.users[@username]
|
26
|
+
raise "Group named '#{@groupname}' found. If this isn't a real group, delete it so that the test can contineu. If this is a real group, change the spec to test with a group that shouldn't exist." if @m.groups[@groupname]
|
27
|
+
rescue Exception => e
|
28
|
+
@fail = true
|
29
|
+
raise e
|
30
|
+
end
|
20
31
|
end
|
21
32
|
|
22
33
|
after(:all) do
|
23
|
-
@
|
24
|
-
|
25
|
-
|
34
|
+
unless @fail
|
35
|
+
@m.remove_user(@username, :quiet => true)
|
36
|
+
@m.remove_group(@username, :quiet => true)
|
37
|
+
@m.remove_group(@groupname, :quiet => true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
after(:each) do
|
42
|
+
unless @fail
|
43
|
+
if @independent
|
44
|
+
@m.remove_user(@username, :quiet => true)
|
45
|
+
@m.remove_group(@username, :quiet => true)
|
46
|
+
@m.remove_group(@groupname, :quiet => true)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def add_user(opts={})
|
52
|
+
# SunOS /home entries don't exist until you add them to auto_home, so
|
53
|
+
# work around this by using a directory we know can be used
|
54
|
+
home = INTERPRETER.tagged?(:sunos) ? "/var/tmp/#{@username}" : nil
|
55
|
+
|
56
|
+
defaults = { :passwd => "asdf", :shell => "/bin/false", :home => home,
|
57
|
+
:quiet => @quiet }
|
58
|
+
|
59
|
+
return @m.add_user(@username, defaults.merge(opts))
|
60
|
+
end
|
61
|
+
|
62
|
+
def add_group
|
63
|
+
return @m.add_group(@groupname)
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_user_with_group
|
67
|
+
add_user
|
68
|
+
return @m.add_group(@groupname, :members => @username)
|
26
69
|
end
|
27
70
|
|
28
71
|
it "should find root user" do
|
@@ -36,27 +79,29 @@ else
|
|
36
79
|
end
|
37
80
|
|
38
81
|
it "should create a user" do
|
39
|
-
entry =
|
82
|
+
entry = add_user
|
40
83
|
|
41
84
|
entry.should_not be_nil
|
42
85
|
entry.name.should == @username
|
43
|
-
# Leaves behind user for further tests
|
44
86
|
end
|
45
87
|
|
46
88
|
it "should have a user after one is created" do
|
47
|
-
|
89
|
+
add_user if @independent
|
90
|
+
|
48
91
|
@m.has_user?(@username).should be_true
|
49
92
|
end
|
50
93
|
|
51
94
|
it "should query user data by name" do
|
52
|
-
|
95
|
+
add_user if @independent
|
96
|
+
|
53
97
|
entry = @m.users[@username]
|
54
98
|
entry.should_not be_nil
|
55
99
|
entry.name.should == @username
|
56
100
|
end
|
57
101
|
|
58
102
|
it "should query user data by id" do
|
59
|
-
|
103
|
+
add_user if @independent
|
104
|
+
|
60
105
|
uid = @m.users[@username].uid
|
61
106
|
|
62
107
|
entry = @m.users[uid]
|
@@ -69,12 +114,14 @@ else
|
|
69
114
|
end
|
70
115
|
|
71
116
|
it "should create user group" do
|
72
|
-
|
117
|
+
add_user if @independent
|
118
|
+
|
73
119
|
@m.groups[@username].should_not be_nil
|
74
120
|
end
|
75
121
|
|
76
122
|
it "should not re-add an existing user" do
|
77
|
-
|
123
|
+
add_user if @independent
|
124
|
+
|
78
125
|
@m.add_user(@username).should be_false
|
79
126
|
end
|
80
127
|
|
@@ -83,23 +130,28 @@ else
|
|
83
130
|
end
|
84
131
|
|
85
132
|
it "should add a group" do
|
86
|
-
entry =
|
133
|
+
entry = add_group
|
134
|
+
|
87
135
|
entry.should_not be_nil
|
88
136
|
entry.name.should == @groupname
|
89
|
-
# Leaves behind group for further tests
|
90
137
|
end
|
91
138
|
|
92
139
|
it "should not re-add a group" do
|
140
|
+
add_group if @independent
|
141
|
+
|
93
142
|
@m.add_group(@groupname).should be_false
|
94
143
|
end
|
95
144
|
|
96
145
|
it "should query group data by name" do
|
146
|
+
add_group if @independent
|
147
|
+
|
97
148
|
entry = @m.groups[@groupname]
|
98
149
|
entry.should_not be_nil
|
99
150
|
entry.name.should == @groupname
|
100
151
|
end
|
101
152
|
|
102
153
|
it "should query group data by id" do
|
154
|
+
add_group if @independent
|
103
155
|
gid = @m.groups[@groupname].gid
|
104
156
|
|
105
157
|
entry = @m.groups[gid]
|
@@ -112,7 +164,8 @@ else
|
|
112
164
|
end
|
113
165
|
|
114
166
|
it "should remove a group" do
|
115
|
-
|
167
|
+
add_group if @independent
|
168
|
+
|
116
169
|
@m.remove_group(@groupname).should be_true
|
117
170
|
end
|
118
171
|
|
@@ -124,41 +177,53 @@ else
|
|
124
177
|
@m.users_for_group(@groupname).should == []
|
125
178
|
end
|
126
179
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
# Leaves behind group for further tests
|
131
|
-
end
|
180
|
+
it "should add a group with members" do
|
181
|
+
add_user_with_group.should_not be_nil
|
182
|
+
end
|
132
183
|
|
133
|
-
|
134
|
-
|
135
|
-
@m.users_for_group(@groupname).should == [@username]
|
136
|
-
end
|
184
|
+
it "should query users in a group" do
|
185
|
+
add_user_with_group if @independent
|
137
186
|
|
138
|
-
|
139
|
-
|
140
|
-
# Depends on group to be created by previous tests
|
141
|
-
@m.groups_for_user(@username).should include(@groupname)
|
142
|
-
end
|
187
|
+
@m.users_for_group(@groupname).should == [@username]
|
188
|
+
end
|
143
189
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
190
|
+
it "should query groups for a user" do
|
191
|
+
add_user_with_group if @independent
|
192
|
+
|
193
|
+
@m.groups_for_user(@username).should include(@groupname)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should remove users from a group" do
|
197
|
+
add_user_with_group if @independent
|
198
|
+
|
199
|
+
@m.remove_users_from_group(@username, @groupname).should == [@username]
|
200
|
+
end
|
149
201
|
|
150
202
|
it "should add groups to a user" do
|
151
|
-
|
203
|
+
add_user if @independent
|
204
|
+
add_group if @independent
|
205
|
+
|
152
206
|
@m.add_groups_to_user(@groupname, @username).should == [@groupname]
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
it "should add users to group" do
|
211
|
+
@m.remove_groups_from_user(@groupname, @username) unless @independent
|
212
|
+
add_user if @independent
|
213
|
+
add_group if @independent
|
214
|
+
|
215
|
+
@m.add_users_to_group(@username, @groupname).should == [@username]
|
153
216
|
end
|
154
217
|
|
155
218
|
it "should remove groups from user" do
|
156
|
-
|
219
|
+
add_user_with_group if @independent
|
220
|
+
|
157
221
|
@m.remove_groups_from_user(@groupname, @username).should == [@groupname]
|
158
222
|
end
|
159
223
|
|
160
224
|
it "should remove a group with members" do
|
161
|
-
|
225
|
+
add_group if @independent
|
226
|
+
|
162
227
|
@m.remove_group(@groupname).should be_true
|
163
228
|
end
|
164
229
|
|
@@ -189,7 +254,7 @@ else
|
|
189
254
|
end
|
190
255
|
|
191
256
|
it "should change password" do
|
192
|
-
|
257
|
+
add_user if @independent
|
193
258
|
pass = "automateit"
|
194
259
|
|
195
260
|
# TODO This isn't portable
|
@@ -201,18 +266,38 @@ else
|
|
201
266
|
end
|
202
267
|
|
203
268
|
before = extract_pwent(@username)
|
204
|
-
@m.passwd(@username, pass).should be_true
|
269
|
+
@m.passwd(@username, pass, :quiet => @quiet).should be_true
|
205
270
|
after = extract_pwent(@username)
|
206
271
|
before.should_not eql(after)
|
207
272
|
end
|
208
273
|
|
209
274
|
it "should remove a user" do
|
210
|
-
|
275
|
+
add_user if @independent
|
211
276
|
@m.remove_user(@username, :quiet => true).should be_true
|
212
277
|
end
|
213
278
|
|
214
279
|
it "should not remove a non-existent user" do
|
215
280
|
@m.remove_user(@username).should be_false
|
216
281
|
end
|
282
|
+
|
283
|
+
it "should add user with multiple groups" do
|
284
|
+
# Find the first few users
|
285
|
+
groups_expected = []
|
286
|
+
size = 3
|
287
|
+
Etc.group do |group|
|
288
|
+
groups_expected << group.name
|
289
|
+
break if groups_expected.size >= size
|
290
|
+
end
|
291
|
+
|
292
|
+
# Create a user
|
293
|
+
(user = add_user(:groups => groups_expected)).should_not be_true
|
294
|
+
|
295
|
+
# Make sure they have the right number of groups
|
296
|
+
groups_found = @m.groups_for_user(@username)
|
297
|
+
for group in groups_expected
|
298
|
+
### puts "%s : %s" % [group, groups_found.include?(group)]
|
299
|
+
groups_found.should include(group)
|
300
|
+
end
|
301
|
+
end
|
217
302
|
end
|
218
303
|
end
|