automateit 0.71021 → 0.71030
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|