automateit 0.70923
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +1 -0
- data/CHANGES.txt +100 -0
- data/Hoe.rake +35 -0
- data/Manifest.txt +111 -0
- data/README.txt +44 -0
- data/Rakefile +284 -0
- data/TESTING.txt +57 -0
- data/TODO.txt +26 -0
- data/TUTORIAL.txt +390 -0
- data/bin/ai +3 -0
- data/bin/aifield +82 -0
- data/bin/aitag +128 -0
- data/bin/automateit +117 -0
- data/docs/friendly_errors.txt +50 -0
- data/docs/previews.txt +86 -0
- data/env.sh +4 -0
- data/examples/basic/Rakefile +26 -0
- data/examples/basic/config/automateit_env.rb +16 -0
- data/examples/basic/config/fields.yml +3 -0
- data/examples/basic/config/tags.yml +13 -0
- data/examples/basic/dist/README.txt +9 -0
- data/examples/basic/dist/myapp_server.erb +30 -0
- data/examples/basic/install.log +15 -0
- data/examples/basic/lib/README.txt +10 -0
- data/examples/basic/recipes/README.txt +4 -0
- data/examples/basic/recipes/install.rb +53 -0
- data/examples/basic/recipes/uninstall.rb +6 -0
- data/gpl.txt +674 -0
- data/lib/automateit.rb +66 -0
- data/lib/automateit/account_manager.rb +106 -0
- data/lib/automateit/account_manager/linux.rb +171 -0
- data/lib/automateit/account_manager/passwd.rb +69 -0
- data/lib/automateit/account_manager/portable.rb +136 -0
- data/lib/automateit/address_manager.rb +165 -0
- data/lib/automateit/address_manager/linux.rb +80 -0
- data/lib/automateit/address_manager/portable.rb +37 -0
- data/lib/automateit/cli.rb +80 -0
- data/lib/automateit/common.rb +65 -0
- data/lib/automateit/constants.rb +33 -0
- data/lib/automateit/edit_manager.rb +292 -0
- data/lib/automateit/error.rb +10 -0
- data/lib/automateit/field_manager.rb +103 -0
- data/lib/automateit/interpreter.rb +641 -0
- data/lib/automateit/package_manager.rb +242 -0
- data/lib/automateit/package_manager/apt.rb +63 -0
- data/lib/automateit/package_manager/egg.rb +64 -0
- data/lib/automateit/package_manager/gem.rb +179 -0
- data/lib/automateit/package_manager/portage.rb +69 -0
- data/lib/automateit/package_manager/yum.rb +65 -0
- data/lib/automateit/platform_manager.rb +47 -0
- data/lib/automateit/platform_manager/darwin.rb +30 -0
- data/lib/automateit/platform_manager/debian.rb +26 -0
- data/lib/automateit/platform_manager/freebsd.rb +25 -0
- data/lib/automateit/platform_manager/gentoo.rb +26 -0
- data/lib/automateit/platform_manager/lsb.rb +40 -0
- data/lib/automateit/platform_manager/struct.rb +78 -0
- data/lib/automateit/platform_manager/uname.rb +29 -0
- data/lib/automateit/platform_manager/windows.rb +33 -0
- data/lib/automateit/plugin.rb +7 -0
- data/lib/automateit/plugin/base.rb +32 -0
- data/lib/automateit/plugin/driver.rb +218 -0
- data/lib/automateit/plugin/manager.rb +232 -0
- data/lib/automateit/project.rb +460 -0
- data/lib/automateit/root.rb +14 -0
- data/lib/automateit/service_manager.rb +79 -0
- data/lib/automateit/service_manager/chkconfig.rb +39 -0
- data/lib/automateit/service_manager/rc_update.rb +37 -0
- data/lib/automateit/service_manager/sysv.rb +126 -0
- data/lib/automateit/service_manager/update_rcd.rb +35 -0
- data/lib/automateit/shell_manager.rb +261 -0
- data/lib/automateit/shell_manager/base_link.rb +67 -0
- data/lib/automateit/shell_manager/link.rb +24 -0
- data/lib/automateit/shell_manager/portable.rb +421 -0
- data/lib/automateit/shell_manager/symlink.rb +32 -0
- data/lib/automateit/shell_manager/which.rb +25 -0
- data/lib/automateit/tag_manager.rb +63 -0
- data/lib/automateit/tag_manager/struct.rb +101 -0
- data/lib/automateit/tag_manager/tag_parser.rb +91 -0
- data/lib/automateit/tag_manager/yaml.rb +29 -0
- data/lib/automateit/template_manager.rb +55 -0
- data/lib/automateit/template_manager/base.rb +172 -0
- data/lib/automateit/template_manager/erb.rb +17 -0
- data/lib/ext/metaclass.rb +17 -0
- data/lib/ext/object.rb +18 -0
- data/lib/hashcache.rb +22 -0
- data/lib/helpful_erb.rb +63 -0
- data/lib/nested_error.rb +33 -0
- data/lib/queued_logger.rb +68 -0
- data/lib/tempster.rb +239 -0
- data/misc/index_gem_repository.rb +303 -0
- data/misc/setup_egg.rb +12 -0
- data/misc/setup_gem_dependencies.sh +7 -0
- data/misc/setup_rubygems.sh +21 -0
- data/misc/which.cmd +6 -0
- data/spec/extras/automateit_service_sysv_test +50 -0
- data/spec/extras/scratch.rb +15 -0
- data/spec/extras/simple_recipe.rb +8 -0
- data/spec/integration/account_manager_spec.rb +218 -0
- data/spec/integration/address_manager_linux_spec.rb +119 -0
- data/spec/integration/address_manager_portable_spec.rb +30 -0
- data/spec/integration/cli_spec.rb +215 -0
- data/spec/integration/examples_spec.rb +54 -0
- data/spec/integration/examples_spec_editor.rb +71 -0
- data/spec/integration/package_manager_spec.rb +104 -0
- data/spec/integration/platform_manager_spec.rb +69 -0
- data/spec/integration/service_manager_sysv_spec.rb +115 -0
- data/spec/integration/shell_manager_spec.rb +471 -0
- data/spec/integration/template_manager_erb_spec.rb +31 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/unit/edit_manager_spec.rb +162 -0
- data/spec/unit/field_manager_spec.rb +79 -0
- data/spec/unit/hashcache_spec.rb +28 -0
- data/spec/unit/interpreter_spec.rb +98 -0
- data/spec/unit/platform_manager_spec.rb +44 -0
- data/spec/unit/plugins_spec.rb +253 -0
- data/spec/unit/tag_manager_spec.rb +189 -0
- data/spec/unit/template_manager_erb_spec.rb +137 -0
- metadata +249 -0
- metadata.gz.sig +0 -0
data/lib/automateit.rb
ADDED
@@ -0,0 +1,66 @@
|
|
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 'open4'
|
19
|
+
require 'erb'
|
20
|
+
|
21
|
+
# Load ActiveSupport pieces individually to save ~0.5s
|
22
|
+
### require 'active_support'
|
23
|
+
require 'active_support/core_ext/blank' # foo.blank?
|
24
|
+
require 'active_support/core_ext/class/attribute_accessors' # cattr_accessor
|
25
|
+
require 'active_support/core_ext/class/inheritable_attributes' # inheritable_cattr_accessor
|
26
|
+
require 'active_support/core_ext/module/aliasing' # alias_method_chain
|
27
|
+
require 'active_support/core_ext/string' # "asdf".demodulize.underscore
|
28
|
+
require 'active_support/clean_logger' # cleans up Logger output
|
29
|
+
require 'active_support/core_ext/symbol' # [:foo, :bar].map(&:to_s)
|
30
|
+
|
31
|
+
# Handle ActiveSupport includes
|
32
|
+
require 'active_support/core_ext/hash/keys' # {:foo => :bar}.stringify_keys
|
33
|
+
Hash.module_eval{include ActiveSupport::CoreExtensions::Hash::Keys}
|
34
|
+
|
35
|
+
# Extensions
|
36
|
+
require 'ext/object.rb'
|
37
|
+
require 'ext/metaclass.rb'
|
38
|
+
|
39
|
+
# Helpers
|
40
|
+
require 'hashcache'
|
41
|
+
require 'queued_logger'
|
42
|
+
require 'tempster'
|
43
|
+
require 'helpful_erb'
|
44
|
+
require 'nested_error'
|
45
|
+
|
46
|
+
# Core
|
47
|
+
require 'automateit/root'
|
48
|
+
require 'automateit/constants'
|
49
|
+
require 'automateit/error'
|
50
|
+
require 'automateit/common'
|
51
|
+
require 'automateit/interpreter'
|
52
|
+
require 'automateit/plugin'
|
53
|
+
require 'automateit/cli'
|
54
|
+
require 'automateit/project'
|
55
|
+
|
56
|
+
# Plugins which must be loaded early
|
57
|
+
require 'automateit/shell_manager'
|
58
|
+
require 'automateit/platform_manager' # requires shell
|
59
|
+
require 'automateit/address_manager' # requires shell
|
60
|
+
require 'automateit/tag_manager' # requires address, platform
|
61
|
+
require 'automateit/field_manager' # requires shell
|
62
|
+
require 'automateit/service_manager' # requires shell
|
63
|
+
require 'automateit/package_manager' # requires shell
|
64
|
+
require 'automateit/template_manager'
|
65
|
+
require 'automateit/edit_manager'
|
66
|
+
require 'automateit/account_manager'
|
@@ -0,0 +1,106 @@
|
|
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
|
+
# Find a user account. Method returns a query helper which takes a
|
7
|
+
# +username+ as an index argument and returns a Struct::Passwd entry as
|
8
|
+
# described in Etc::getpwent if the user exists or a nil if not.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# users["root"] # => #<struct Struct::Passwd name="root"...
|
12
|
+
#
|
13
|
+
# users["does_not_exist"] # => nil
|
14
|
+
def users() dispatch() end
|
15
|
+
|
16
|
+
# Add the +username+ if not already created.
|
17
|
+
#
|
18
|
+
# Options:
|
19
|
+
# * :description -- User's full name. Defaults to username.
|
20
|
+
# * :home -- Path to user's home directory. If not specified, uses system
|
21
|
+
# default like "/home/username".
|
22
|
+
# * :create_home -- Create homedir. Defaults to true.
|
23
|
+
# * :groups -- Array of group names to add this user to.
|
24
|
+
# * :shell -- Path to login shell. If not specified, uses system default
|
25
|
+
# like "/bin/bash".
|
26
|
+
# * :uid -- Fixnum user ID for user. Default chooses an unused id.
|
27
|
+
# * :gid -- Fixnum group ID for user. Default chooses same gid as uid.
|
28
|
+
#
|
29
|
+
# Example:
|
30
|
+
# add_user("bob", :description => "Bob Smith")
|
31
|
+
def add_user(username, opts={}) dispatch(username, opts) end
|
32
|
+
|
33
|
+
# TODO AccountManager#update_user -- implement
|
34
|
+
### def update_user(username, opts={}) dispatch(username, opts) end
|
35
|
+
|
36
|
+
# Remove the +username+ if present.
|
37
|
+
#
|
38
|
+
# Options:
|
39
|
+
# * :remove_home -- Delete user's home directory and mail spool. Default is
|
40
|
+
# true.
|
41
|
+
def remove_user(username, opts={}) dispatch(username, opts) end
|
42
|
+
|
43
|
+
# Is +user+ present?
|
44
|
+
def has_user?(user) dispatch(user) end
|
45
|
+
|
46
|
+
# Add +groups+ (array of groupnames) to +user+.
|
47
|
+
def add_groups_to_user(groups, user) dispatch(groups, user) end
|
48
|
+
|
49
|
+
# Remove +groups+ (array of groupnames) from +user+.
|
50
|
+
def remove_groups_from_user(groups, user) dispatch(groups, user) end
|
51
|
+
|
52
|
+
# Change the +password+ for the +user+.
|
53
|
+
def passwd(user, password) dispatch(user, password) end
|
54
|
+
|
55
|
+
#.......................................................................
|
56
|
+
|
57
|
+
# Find a group. Method returns a query helper which takes a
|
58
|
+
# +groupname+ as an index argument and returns a Struct::Group entry as
|
59
|
+
# described in Etc::getgrent if the group exists or a nil if not.
|
60
|
+
#
|
61
|
+
# Example:
|
62
|
+
# groups["root"] # => #<struct Struct::Group name="root"...
|
63
|
+
#
|
64
|
+
# groups["does_not_exist"] # => nil
|
65
|
+
def groups() dispatch() end
|
66
|
+
|
67
|
+
# Add +groupname+ if it doesn't exist. Options:
|
68
|
+
# * :members -- Array of usernames to add as members.
|
69
|
+
# * :gid -- Group ID to use. Default is to find an unused id.
|
70
|
+
def add_group(groupname, opts={}) dispatch(groupname, opts) end
|
71
|
+
|
72
|
+
# TODO AccountManager#update_group -- implement
|
73
|
+
### def update_group(groupname, opts={}) dispatch(groupname, opts) end
|
74
|
+
|
75
|
+
# Remove +groupname+ if it exists.
|
76
|
+
def remove_group(groupname, opts={}) dispatch(groupname, opts) end
|
77
|
+
|
78
|
+
# Does +group+ exist?
|
79
|
+
def has_group?(group) dispatch(group) end
|
80
|
+
|
81
|
+
# Add +users+ (array of usernames) to +group+.
|
82
|
+
def add_users_to_group(users, group) dispatch(users, group) end
|
83
|
+
|
84
|
+
# Remove +users+ (array of usernames) from +group+.
|
85
|
+
def remove_users_from_group(users, group) dispatch(users, group) end
|
86
|
+
|
87
|
+
# Array of groupnames this user is a member of.
|
88
|
+
def groups_for_user(query) dispatch(query) end
|
89
|
+
|
90
|
+
# Array of usernames in group.
|
91
|
+
def users_for_group(query) dispatch(query) end
|
92
|
+
|
93
|
+
# Hash of usernames and the groupnames they're members of.
|
94
|
+
def users_to_groups() dispatch() end
|
95
|
+
end # class AccountManager
|
96
|
+
|
97
|
+
# == AccountManager::BaseDriver
|
98
|
+
#
|
99
|
+
# Base class for all AccountManager drivers.
|
100
|
+
class ::AutomateIt::AccountManager::BaseDriver < AutomateIt::Plugin::Driver
|
101
|
+
end
|
102
|
+
|
103
|
+
# Drivers
|
104
|
+
require 'automateit/account_manager/portable'
|
105
|
+
require 'automateit/account_manager/linux'
|
106
|
+
require 'automateit/account_manager/passwd'
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# == AccountManager::Linux
|
2
|
+
#
|
3
|
+
# A Linux-specific driver for the AccountManager.
|
4
|
+
class ::AutomateIt::AccountManager::Linux < ::AutomateIt::AccountManager::Portable
|
5
|
+
depends_on \
|
6
|
+
:programs => %w(useradd usermod userdel groupadd groupmod groupdel),
|
7
|
+
:callbacks => [lambda{AutomateIt::AccountManager::Portable.has_etc?}]
|
8
|
+
|
9
|
+
def suitability(method, *args) # :nodoc:
|
10
|
+
# Level must be higher than Portable
|
11
|
+
return available? ? 2 : 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup(*args) # :nodoc:
|
15
|
+
super(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Is "nscd" available on this platform?
|
19
|
+
def nscd?
|
20
|
+
@nscd ||= interpreter.which("nscd")
|
21
|
+
end
|
22
|
+
|
23
|
+
#.......................................................................
|
24
|
+
|
25
|
+
# See AccountManager#add_user
|
26
|
+
def add_user(username, opts={})
|
27
|
+
return false if has_user?(username)
|
28
|
+
cmd = "useradd"
|
29
|
+
cmd << " --comment #{opts[:description] || username}"
|
30
|
+
cmd << " --home #{opts[:home]}" if opts[:home]
|
31
|
+
cmd << " --create-home" unless opts[:create_home] == false
|
32
|
+
cmd << " --groups #{opts[:groups].join(' ')}" if opts[:groups]
|
33
|
+
cmd << " --shell #{opts[:shell] || "/bin/bash"}"
|
34
|
+
cmd << " --uid #{opts[:uid]}" if opts[:uid]
|
35
|
+
cmd << " --gid #{opts[:gid]}" if opts[:gid]
|
36
|
+
cmd << " #{username} < /dev/null"
|
37
|
+
cmd << " > /dev/null" if opts[:quiet]
|
38
|
+
interpreter.sh(cmd)
|
39
|
+
interpreter.sh("nscd --invalidate passwd") if nscd?
|
40
|
+
|
41
|
+
unless opts[:group] == false
|
42
|
+
groupname = opts[:group] || username
|
43
|
+
unless has_group?(groupname)
|
44
|
+
opts = {:members => [username]}
|
45
|
+
# In preview mode, user doesn't exist and has no UID
|
46
|
+
opts[:gid] = users[username].uid if writing?
|
47
|
+
add_group(groupname, opts)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
interpreter.account_manager.passwd(username, opts[:passwd]) if opts[:passwd]
|
52
|
+
|
53
|
+
return users[username]
|
54
|
+
end
|
55
|
+
|
56
|
+
# TODO AccountManager#update_user -- implement
|
57
|
+
### def update_user(username, opts={}) dispatch(username, opts) end
|
58
|
+
|
59
|
+
# See AccountManager#remove_user
|
60
|
+
def remove_user(username, opts={})
|
61
|
+
return false unless has_user?(username)
|
62
|
+
# Options: -r -- remove the home directory and mail spool
|
63
|
+
cmd = "userdel"
|
64
|
+
cmd << " -r" unless opts[:remove_home] == false
|
65
|
+
cmd << " #{username}"
|
66
|
+
cmd << " > /dev/null" if opts[:quiet]
|
67
|
+
interpreter.sh(cmd)
|
68
|
+
interpreter.sh("nscd --invalidate passwd") if nscd?
|
69
|
+
remove_group(username) if has_group?(username)
|
70
|
+
return true
|
71
|
+
end
|
72
|
+
|
73
|
+
# See AccountManager#add_groups_to_user
|
74
|
+
def add_groups_to_user(groups, username)
|
75
|
+
groups = [groups].flatten
|
76
|
+
present = groups_for_user(username)
|
77
|
+
missing = groups - present
|
78
|
+
return false if missing.empty?
|
79
|
+
|
80
|
+
cmd = "usermod -a -G #{missing.join(',')} #{username}"
|
81
|
+
interpreter.sh(cmd)
|
82
|
+
interpreter.sh("nscd --invalidate group") if nscd?
|
83
|
+
return missing
|
84
|
+
end
|
85
|
+
|
86
|
+
# See AccountManager#remove_groups_from_user
|
87
|
+
def remove_groups_from_user(groups, username)
|
88
|
+
groups = [groups].flatten
|
89
|
+
present = groups_for_user(username)
|
90
|
+
removeable = groups & present
|
91
|
+
return false if removeable.empty?
|
92
|
+
|
93
|
+
cmd = "usermod -G #{(present-groups).join(',')} #{username}"
|
94
|
+
interpreter.sh(cmd)
|
95
|
+
interpreter.sh("nscd --invalidate group") if nscd?
|
96
|
+
return removeable
|
97
|
+
end
|
98
|
+
|
99
|
+
#.......................................................................
|
100
|
+
|
101
|
+
# See AccountManager#add_group
|
102
|
+
def add_group(groupname, opts={})
|
103
|
+
return false if has_group?(groupname)
|
104
|
+
cmd = "groupadd"
|
105
|
+
cmd << " -g #{opts[:gid]}" if opts[:gid]
|
106
|
+
cmd << " #{groupname}"
|
107
|
+
interpreter.sh(cmd)
|
108
|
+
interpreter.sh("nscd --invalidate group") if nscd?
|
109
|
+
add_users_to_group(opts[:members], groupname) if opts[:members]
|
110
|
+
return groups[groupname]
|
111
|
+
end
|
112
|
+
|
113
|
+
# TODO AccountManager#update_group -- implement
|
114
|
+
### def update_group(groupname, opts={}) dispatch(groupname, opts) end
|
115
|
+
|
116
|
+
# See AccountManager#remove_group
|
117
|
+
def remove_group(groupname, opts={})
|
118
|
+
return false unless has_group?(groupname)
|
119
|
+
cmd = "groupdel #{groupname}"
|
120
|
+
interpreter.sh(cmd)
|
121
|
+
interpreter.sh("nscd --invalidate group") if nscd?
|
122
|
+
return true
|
123
|
+
end
|
124
|
+
|
125
|
+
# See AccountManager#add_users_to_group
|
126
|
+
def add_users_to_group(users, groupname)
|
127
|
+
users = [users].flatten
|
128
|
+
# XXX Include pwent.gid?
|
129
|
+
grent = groups[groupname]
|
130
|
+
missing = \
|
131
|
+
if writing? and not grent
|
132
|
+
raise ArgumentError.new("no such group: #{groupname}")
|
133
|
+
elsif writing? or grent
|
134
|
+
users - grent.mem
|
135
|
+
else
|
136
|
+
users
|
137
|
+
end
|
138
|
+
return false if missing.empty?
|
139
|
+
|
140
|
+
for username in missing
|
141
|
+
cmd = "usermod -a -G #{groupname} #{username}"
|
142
|
+
interpreter.sh(cmd)
|
143
|
+
end
|
144
|
+
interpreter.sh("nscd --invalidate group") if nscd?
|
145
|
+
return missing
|
146
|
+
end
|
147
|
+
|
148
|
+
# See AccountManager#remove_users_from_group
|
149
|
+
def remove_users_from_group(users, groupname)
|
150
|
+
users = [users].flatten
|
151
|
+
grent = groups[groupname]
|
152
|
+
present = \
|
153
|
+
if writing? and not grent
|
154
|
+
raise ArgumentError.new("no such group: #{groupname}")
|
155
|
+
elsif writing? or grent
|
156
|
+
grent.mem & users
|
157
|
+
else
|
158
|
+
users
|
159
|
+
end
|
160
|
+
return false if present.empty?
|
161
|
+
|
162
|
+
u2g = users_to_groups
|
163
|
+
for username in present
|
164
|
+
user_groups = u2g[username]
|
165
|
+
cmd = "usermod -G #{(user_groups.to_a-[groupname]).join(',')} #{username}"
|
166
|
+
interpreter.sh(cmd)
|
167
|
+
end
|
168
|
+
interpreter.sh("nscd --invalidate group") if nscd?
|
169
|
+
return present
|
170
|
+
end
|
171
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# == AccountManager::Passwd
|
2
|
+
#
|
3
|
+
# An AccountManager driver for the +passwd+ command found on Unix-like systems.
|
4
|
+
class ::AutomateIt::AccountManager::Passwd < ::AutomateIt::AccountManager::BaseDriver
|
5
|
+
depends_on \
|
6
|
+
:programs => %w(passwd),
|
7
|
+
:libraries => %w(open3 expect pty)
|
8
|
+
|
9
|
+
def suitability(method, *args) # :nodoc:
|
10
|
+
# Level must be higher than Linux
|
11
|
+
return available? ? 3 : 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# See AccountManager#passwd
|
15
|
+
def passwd(user, password, opts={})
|
16
|
+
users = interpreter.account_manager.users
|
17
|
+
|
18
|
+
unless users[user]
|
19
|
+
if preview?
|
20
|
+
log.info(PNOTE+"Setting password for user: #{user}")
|
21
|
+
return true
|
22
|
+
else
|
23
|
+
raise ArgumentError.new("No such user: #{user}")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
case user
|
28
|
+
when Symbol: user = user.to_s
|
29
|
+
when Integer: user = users[user]
|
30
|
+
when String: # leave it alone
|
31
|
+
else raise TypeError.new("Unknown user type: #{user.class}")
|
32
|
+
end
|
33
|
+
|
34
|
+
tries = 5
|
35
|
+
exitstatus = nil
|
36
|
+
begin
|
37
|
+
exitstruct = _passwd_raw(user, password, opts)
|
38
|
+
if exitstatus and not exitstruct.exitstatus.zero?
|
39
|
+
# 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.
|
40
|
+
raise Errno::EPIPE.new("bad exitstatus %s" % exitstruct.exitstatus)
|
41
|
+
end
|
42
|
+
rescue Errno::EPIPE => e
|
43
|
+
# 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?
|
44
|
+
if tries <= 0
|
45
|
+
raise e
|
46
|
+
else
|
47
|
+
tries -= 1
|
48
|
+
retry
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
return exitstruct.exitstatus.zero?
|
53
|
+
end
|
54
|
+
|
55
|
+
def _passwd_raw(user, password, opts={})
|
56
|
+
quiet = (opts[:quiet] or not log.info?)
|
57
|
+
|
58
|
+
return Open4::popen4("passwd %s 2>&1" % user) do |pid, sin, sout, serr|
|
59
|
+
$expect_verbose = ! quiet
|
60
|
+
2.times do
|
61
|
+
sout.expect(/:/)
|
62
|
+
sleep 0.1 # Reduce chance of passwd thinking we're a robot :(
|
63
|
+
sin.puts password
|
64
|
+
puts "*" * 12 unless quiet
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
protected :_passwd_raw
|
69
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# == AccountManager::Portable
|
2
|
+
#
|
3
|
+
# A pure-Ruby, portable driver for the AccountManager. It is only suitable
|
4
|
+
# for doing queries and lacks methods such as +add_user+. Platform-specific
|
5
|
+
# drivers inherit from this class and provide these methods.
|
6
|
+
class ::AutomateIt::AccountManager::Portable < ::AutomateIt::AccountManager::BaseDriver
|
7
|
+
depends_on :callbacks => [lambda{AutomateIt::AccountManager::Portable.has_etc?}]
|
8
|
+
|
9
|
+
def suitability(method, *args) # :nodoc:
|
10
|
+
return 1
|
11
|
+
end
|
12
|
+
|
13
|
+
# Does this platform provide a way of querying users and groups through
|
14
|
+
# the 'etc' module?
|
15
|
+
def self.has_etc?
|
16
|
+
begin
|
17
|
+
require 'etc'
|
18
|
+
return defined?(Etc)
|
19
|
+
rescue LoadError
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Alias for AccountManager::Portable.has_etc?
|
25
|
+
def has_etc?
|
26
|
+
self.has_etc?
|
27
|
+
end
|
28
|
+
|
29
|
+
#.......................................................................
|
30
|
+
|
31
|
+
# == UserQuery
|
32
|
+
#
|
33
|
+
# A class used for querying users. See AccountManager#users.
|
34
|
+
class UserQuery
|
35
|
+
# See AccountManager#users
|
36
|
+
def [](query)
|
37
|
+
Etc.endpwent
|
38
|
+
begin
|
39
|
+
case query
|
40
|
+
when String
|
41
|
+
return Etc.getpwnam(query)
|
42
|
+
when Fixnum
|
43
|
+
return Etc.getpwuid(query)
|
44
|
+
else
|
45
|
+
raise TypeError.new("unknonwn type for query: #{query.class}")
|
46
|
+
end
|
47
|
+
rescue ArgumentError
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# See AccountManager#users
|
54
|
+
def users
|
55
|
+
return UserQuery.new
|
56
|
+
end
|
57
|
+
|
58
|
+
# See AccountManager#has_user?
|
59
|
+
def has_user?(query)
|
60
|
+
return ! users[query].nil?
|
61
|
+
end
|
62
|
+
|
63
|
+
#.......................................................................
|
64
|
+
|
65
|
+
# == GroupQuery
|
66
|
+
#
|
67
|
+
# A class used for querying groups. See AccountManager#groups.
|
68
|
+
class GroupQuery
|
69
|
+
# See AccountManager#groups
|
70
|
+
def [](query)
|
71
|
+
Etc.endgrent
|
72
|
+
begin
|
73
|
+
case query
|
74
|
+
when String
|
75
|
+
return Etc.getgrnam(query)
|
76
|
+
when Fixnum
|
77
|
+
return Etc.getgrgid(query)
|
78
|
+
else
|
79
|
+
raise TypeError.new("unknonwn type for query: #{query.class}")
|
80
|
+
end
|
81
|
+
rescue ArgumentError
|
82
|
+
return nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# See AccountManager#groups
|
88
|
+
def groups
|
89
|
+
return GroupQuery.new
|
90
|
+
end
|
91
|
+
|
92
|
+
# See AccountManager#has_group?
|
93
|
+
def has_group?(query)
|
94
|
+
return ! groups[query].nil?
|
95
|
+
end
|
96
|
+
|
97
|
+
# See AccountManager#groups_for_user
|
98
|
+
def groups_for_user(query)
|
99
|
+
pwent = users[query]
|
100
|
+
return [] if preview? and not pwent
|
101
|
+
username = pwent.name
|
102
|
+
result = Set.new
|
103
|
+
result << groups[pwent.gid].name if groups[pwent.gid]
|
104
|
+
Etc.group do |grent|
|
105
|
+
result << grent.name if grent.mem.include?(username)
|
106
|
+
end
|
107
|
+
return result.to_a
|
108
|
+
end
|
109
|
+
|
110
|
+
# See AccountManager#users_for_group
|
111
|
+
def users_for_group(query)
|
112
|
+
grent = groups[query]
|
113
|
+
return (preview? || ! grent) ? [] : grent.mem
|
114
|
+
end
|
115
|
+
|
116
|
+
# See AccountManager#users_to_groups
|
117
|
+
def users_to_groups
|
118
|
+
result = {}
|
119
|
+
Etc.group do |grent|
|
120
|
+
grent.mem.each do |username|
|
121
|
+
result[username] ||= Set.new
|
122
|
+
result[username] << grent.name
|
123
|
+
end
|
124
|
+
end
|
125
|
+
Etc.passwd do |pwent|
|
126
|
+
grent = groups[pwent.gid]
|
127
|
+
unless grent
|
128
|
+
log.fatal(PNOTE+"WARNING: User's default group doesn't exist: user %s, gid %s" % [pwent.name, pwent.gid])
|
129
|
+
next
|
130
|
+
end
|
131
|
+
result[pwent.name] ||= Set.new
|
132
|
+
result[pwent.name] << grent.name
|
133
|
+
end
|
134
|
+
return result
|
135
|
+
end
|
136
|
+
end
|