puppet 2.6.8 → 2.6.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puppet might be problematic. Click here for more details.
- data/CHANGELOG +41 -0
- data/{README.rst → README.md} +17 -10
- data/conf/redhat/puppet.spec +93 -22
- data/ext/vim/README +2 -1
- data/ext/vim/ftplugin/puppet.vim +94 -0
- data/ext/vim/indent/puppet.vim +76 -0
- data/lib/puppet.rb +2 -3
- data/lib/puppet/application/agent.rb +3 -3
- data/lib/puppet/application/apply.rb +16 -5
- data/lib/puppet/configurer.rb +60 -56
- data/lib/puppet/configurer/fact_handler.rb +6 -1
- data/lib/puppet/defaults.rb +19 -0
- data/lib/puppet/file_serving/fileset.rb +1 -1
- data/lib/puppet/indirector/exec.rb +3 -3
- data/lib/puppet/indirector/queue.rb +1 -1
- data/lib/puppet/metatype/manager.rb +7 -20
- data/lib/puppet/parser/compiler.rb +17 -16
- data/lib/puppet/parser/resource.rb +18 -1
- data/lib/puppet/parser/scope.rb +2 -2
- data/lib/puppet/provider/mount/parsed.rb +1 -1
- data/lib/puppet/provider/naginator.rb +9 -1
- data/lib/puppet/provider/nameservice/directoryservice.rb +11 -8
- data/lib/puppet/provider/package/aptitude.rb +1 -0
- data/lib/puppet/rails/host.rb +7 -0
- data/lib/puppet/resource/catalog.rb +9 -3
- data/lib/puppet/transaction.rb +15 -26
- data/lib/puppet/transaction/report.rb +3 -3
- data/lib/puppet/type.rb +13 -24
- data/lib/puppet/util/queue.rb +1 -1
- data/lib/puppet/util/queue/stomp.rb +2 -2
- data/lib/puppet/util/settings/file_setting.rb +1 -0
- data/spec/integration/defaults_spec.rb +22 -0
- data/spec/integration/indirector/catalog/queue_spec.rb +2 -2
- data/spec/integration/type_spec.rb +11 -0
- data/spec/unit/application/agent_spec.rb +2 -2
- data/spec/unit/application/apply_spec.rb +62 -50
- data/spec/unit/configurer/fact_handler_spec.rb +43 -37
- data/spec/unit/configurer_spec.rb +404 -327
- data/spec/unit/file_serving/fileset_spec.rb +7 -0
- data/spec/unit/indirector/exec_spec.rb +4 -4
- data/spec/unit/indirector/node/exec_spec.rb +1 -1
- data/spec/unit/indirector/queue_spec.rb +4 -4
- data/spec/unit/node_spec.rb +1 -0
- data/spec/unit/parser/compiler_spec.rb +8 -46
- data/spec/unit/parser/resource_spec.rb +61 -3
- data/spec/unit/parser/scope_spec.rb +9 -3
- data/spec/unit/provider/nameservice/directoryservice_spec.rb +60 -0
- data/spec/unit/rails/host_spec.rb +8 -0
- data/spec/unit/resource/catalog_spec.rb +1 -1
- data/spec/unit/transaction/report_spec.rb +3 -3
- data/spec/unit/transaction_spec.rb +8 -2
- data/spec/unit/type_spec.rb +66 -0
- data/spec/unit/util/queue/stomp_spec.rb +10 -10
- data/spec/unit/util/settings/file_setting_spec.rb +4 -0
- metadata +1229 -1232
- data/README +0 -31
- data/lib/puppet/provider/nameservice/#directoryservice.rb# +0 -519
- data/lib/puppet/reference/#providers.rb# +0 -123
- data/spec/unit/indirector/certificate_status/#file_spec.rb# +0 -188
- data/spec/unit/resource/#type_collection_spec.rb# +0 -463
data/README
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
Documentation (and detailed install instructions) can be found
|
2
|
-
online at http://docs.puppetlabs.com/.
|
3
|
-
|
4
|
-
Additional documentation can also be found at the Puppet Wiki:
|
5
|
-
|
6
|
-
http://projects.puppetlabs.com/projects/puppet/wiki/
|
7
|
-
|
8
|
-
Generally, you need the following things installed:
|
9
|
-
|
10
|
-
* Ruby >= 1.8.1 (earlier releases might work but probably not)
|
11
|
-
|
12
|
-
* The Ruby OpenSSL library. For some reason, this often isn't included
|
13
|
-
in the main ruby distributions. You can test for it by running
|
14
|
-
'ruby -ropenssl -e "puts :yep"'. If that errors out, you're missing the
|
15
|
-
library.
|
16
|
-
|
17
|
-
If your distribution doesn't come with the necessary library (e.g., on Debian
|
18
|
-
and Ubuntu you need to install libopenssl-ruby), then you'll probably have to
|
19
|
-
compile Ruby yourself, since it's part of the standard library and not
|
20
|
-
available separately. You could probably just compile and install that one
|
21
|
-
library, though.
|
22
|
-
|
23
|
-
* The Ruby XMLRPC client and server libraries. For some reason, this often
|
24
|
-
isn't included in the main ruby distributions. You can test for it by
|
25
|
-
running 'ruby -rxmlrpc/client -e "puts :yep"'. If that errors out, you're missing
|
26
|
-
the library.
|
27
|
-
|
28
|
-
* Facter => 1.5.1
|
29
|
-
You can get this from < http://puppetlabs.com/projects/facter >
|
30
|
-
|
31
|
-
$Id$
|
@@ -1,519 +0,0 @@
|
|
1
|
-
# Created by Jeff McCune on 2007-07-22
|
2
|
-
# Copyright (c) 2007. All rights reserved.
|
3
|
-
#
|
4
|
-
# This program is free software; you can redistribute it and/or
|
5
|
-
# modify it under the terms of the GNU General Public License
|
6
|
-
# as published by the Free Software Foundation (version 2 of the License)
|
7
|
-
# This program is distributed in the hope that it will be useful,
|
8
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
9
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
10
|
-
# GNU General Public License for more details.
|
11
|
-
# You should have received a copy of the GNU General Public License
|
12
|
-
# along with this program; if not, write to the Free Software
|
13
|
-
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston MA 02110-1301 USA
|
14
|
-
|
15
|
-
require 'puppet'
|
16
|
-
require 'puppet/provider/nameservice'
|
17
|
-
require 'facter/util/plist'
|
18
|
-
require 'cgi'
|
19
|
-
|
20
|
-
|
21
|
-
class Puppet::Provider::NameService
|
22
|
-
class DirectoryService < Puppet::Provider::NameService
|
23
|
-
# JJM: Dive into the singleton_class
|
24
|
-
class << self
|
25
|
-
# JJM: This allows us to pass information when calling
|
26
|
-
# Puppet::Type.type
|
27
|
-
# e.g. Puppet::Type.type(:user).provide :directoryservice, :ds_path => "Users"
|
28
|
-
# This is referenced in the get_ds_path class method
|
29
|
-
attr_writer :ds_path
|
30
|
-
attr_writer :macosx_version_major
|
31
|
-
end
|
32
|
-
|
33
|
-
initvars
|
34
|
-
|
35
|
-
commands :dscl => "/usr/bin/dscl"
|
36
|
-
commands :dseditgroup => "/usr/sbin/dseditgroup"
|
37
|
-
commands :sw_vers => "/usr/bin/sw_vers"
|
38
|
-
confine :operatingsystem => :darwin
|
39
|
-
defaultfor :operatingsystem => :darwin
|
40
|
-
|
41
|
-
|
42
|
-
# JJM 2007-07-25: This map is used to map NameService attributes to their
|
43
|
-
# corresponding DirectoryService attribute names.
|
44
|
-
# See: http://images.apple.com/server/docs.Open_Directory_v10.4.pdf
|
45
|
-
# JJM: Note, this is de-coupled from the Puppet::Type, and must
|
46
|
-
# be actively maintained. There may also be collisions with different
|
47
|
-
# types (Users, Groups, Mounts, Hosts, etc...)
|
48
|
-
@@ds_to_ns_attribute_map = {
|
49
|
-
'RecordName' => :name,
|
50
|
-
'PrimaryGroupID' => :gid,
|
51
|
-
'NFSHomeDirectory' => :home,
|
52
|
-
'UserShell' => :shell,
|
53
|
-
'UniqueID' => :uid,
|
54
|
-
'RealName' => :comment,
|
55
|
-
'Password' => :password,
|
56
|
-
'GeneratedUID' => :guid,
|
57
|
-
'IPAddress' => :ip_address,
|
58
|
-
'ENetAddress' => :en_address,
|
59
|
-
'GroupMembership' => :members,
|
60
|
-
}
|
61
|
-
# JJM The same table as above, inverted.
|
62
|
-
@@ns_to_ds_attribute_map = {
|
63
|
-
:name => 'RecordName',
|
64
|
-
:gid => 'PrimaryGroupID',
|
65
|
-
:home => 'NFSHomeDirectory',
|
66
|
-
:shell => 'UserShell',
|
67
|
-
:uid => 'UniqueID',
|
68
|
-
:comment => 'RealName',
|
69
|
-
:password => 'Password',
|
70
|
-
:guid => 'GeneratedUID',
|
71
|
-
:en_address => 'ENetAddress',
|
72
|
-
:ip_address => 'IPAddress',
|
73
|
-
:members => 'GroupMembership',
|
74
|
-
}
|
75
|
-
|
76
|
-
@@password_hash_dir = "/var/db/shadow/hash"
|
77
|
-
|
78
|
-
def self.instances
|
79
|
-
# JJM Class method that provides an array of instance objects of this
|
80
|
-
# type.
|
81
|
-
# JJM: Properties are dependent on the Puppet::Type we're managine.
|
82
|
-
type_property_array = [:name] + @resource_type.validproperties
|
83
|
-
|
84
|
-
# Create a new instance of this Puppet::Type for each object present
|
85
|
-
# on the system.
|
86
|
-
list_all_present.collect do |name_string|
|
87
|
-
self.new(single_report(name_string, *type_property_array))
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def self.get_ds_path
|
92
|
-
# JJM: 2007-07-24 This method dynamically returns the DS path we're concerned with.
|
93
|
-
# For example, if we're working with an user type, this will be /Users
|
94
|
-
# with a group type, this will be /Groups.
|
95
|
-
# @ds_path is an attribute of the class itself.
|
96
|
-
return @ds_path if defined?(@ds_path)
|
97
|
-
# JJM: "Users" or "Groups" etc ... (Based on the Puppet::Type)
|
98
|
-
# Remember this is a class method, so self.class is Class
|
99
|
-
# Also, @resource_type seems to be the reference to the
|
100
|
-
# Puppet::Type this class object is providing for.
|
101
|
-
@resource_type.name.to_s.capitalize + "s"
|
102
|
-
end
|
103
|
-
|
104
|
-
def self.get_macosx_version_major
|
105
|
-
return @macosx_version_major if defined?(@macosx_version_major)
|
106
|
-
begin
|
107
|
-
# Make sure we've loaded all of the facts
|
108
|
-
Facter.loadfacts
|
109
|
-
|
110
|
-
if Facter.value(:macosx_productversion_major)
|
111
|
-
product_version_major = Facter.value(:macosx_productversion_major)
|
112
|
-
else
|
113
|
-
# TODO: remove this code chunk once we require Facter 1.5.5 or higher.
|
114
|
-
Puppet.warning("DEPRECATION WARNING: Future versions of the directoryservice provider will require Facter 1.5.5 or newer.")
|
115
|
-
product_version = Facter.value(:macosx_productversion)
|
116
|
-
fail("Could not determine OS X version from Facter") if product_version.nil?
|
117
|
-
product_version_major = product_version.scan(/(\d+)\.(\d+)./).join(".")
|
118
|
-
end
|
119
|
-
fail("#{product_version_major} is not supported by the directoryservice provider") if %w{10.0 10.1 10.2 10.3}.include?(product_version_major)
|
120
|
-
@macosx_version_major = product_version_major
|
121
|
-
return @macosx_version_major
|
122
|
-
rescue Puppet::ExecutionFailure => detail
|
123
|
-
fail("Could not determine OS X version: #{detail}")
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
|
128
|
-
def self.list_all_present
|
129
|
-
# JJM: List all objects of this Puppet::Type already present on the system.
|
130
|
-
begin
|
131
|
-
dscl_output = execute(get_exec_preamble("-list"))
|
132
|
-
rescue Puppet::ExecutionFailure => detail
|
133
|
-
fail("Could not get #{@resource_type.name} list from DirectoryService")
|
134
|
-
end
|
135
|
-
dscl_output.split("\n")
|
136
|
-
end
|
137
|
-
|
138
|
-
def self.parse_dscl_url_data(dscl_output)
|
139
|
-
# we need to construct a Hash from the dscl -url output to match
|
140
|
-
# that returned by the dscl -plist output for 10.5+ clients.
|
141
|
-
#
|
142
|
-
# Nasty assumptions:
|
143
|
-
# a) no values *end* in a colon ':', only keys
|
144
|
-
# b) if a line ends in a colon and the next line does start with
|
145
|
-
# a space, then the second line is a value of the first.
|
146
|
-
# c) (implied by (b)) keys don't start with spaces.
|
147
|
-
|
148
|
-
dscl_plist = {}
|
149
|
-
dscl_output.split("\n").inject([]) do |array, line|
|
150
|
-
if line =~ /^\s+/ # it's a value
|
151
|
-
array[-1] << line # add the value to the previous key
|
152
|
-
else
|
153
|
-
array << line
|
154
|
-
end
|
155
|
-
array
|
156
|
-
end.compact
|
157
|
-
|
158
|
-
dscl_output.each do |line|
|
159
|
-
# This should be a 'normal' entry. key and value on one line.
|
160
|
-
# We split on ': ' to deal with keys/values with a colon in them.
|
161
|
-
split_array = line.split(/:\s+/)
|
162
|
-
key = split_array.first
|
163
|
-
value = CGI::unescape(split_array.last.strip.chomp)
|
164
|
-
# We need to treat GroupMembership separately as it is currently
|
165
|
-
# the only attribute we care about multiple values for, and
|
166
|
-
# the values can never contain spaces (shortnames)
|
167
|
-
# We also make every value an array to be consistent with the
|
168
|
-
# output of dscl -plist under 10.5
|
169
|
-
if key == "GroupMembership"
|
170
|
-
dscl_plist[key] = value.split(/\s/)
|
171
|
-
else
|
172
|
-
dscl_plist[key] = [value]
|
173
|
-
end
|
174
|
-
end
|
175
|
-
dscl_plist
|
176
|
-
end
|
177
|
-
|
178
|
-
def self.parse_dscl_plist_data(dscl_output)
|
179
|
-
Plist.parse_xml(dscl_output)
|
180
|
-
end
|
181
|
-
|
182
|
-
def self.generate_attribute_hash(input_hash, *type_properties)
|
183
|
-
attribute_hash = {}
|
184
|
-
input_hash.keys.each do |key|
|
185
|
-
ds_attribute = key.sub("dsAttrTypeStandard:", "")
|
186
|
-
next unless (@@ds_to_ns_attribute_map.keys.include?(ds_attribute) and type_properties.include? @@ds_to_ns_attribute_map[ds_attribute])
|
187
|
-
ds_value = input_hash[key]
|
188
|
-
case @@ds_to_ns_attribute_map[ds_attribute]
|
189
|
-
when :members
|
190
|
-
ds_value = ds_value # only members uses arrays so far
|
191
|
-
when :gid, :uid
|
192
|
-
# OS X stores objects like uid/gid as strings.
|
193
|
-
# Try casting to an integer for these cases to be
|
194
|
-
# consistent with the other providers and the group type
|
195
|
-
# validation
|
196
|
-
begin
|
197
|
-
ds_value = Integer(ds_value[0])
|
198
|
-
rescue ArgumentError
|
199
|
-
ds_value = ds_value[0]
|
200
|
-
end
|
201
|
-
else ds_value = ds_value[0]
|
202
|
-
end
|
203
|
-
attribute_hash[@@ds_to_ns_attribute_map[ds_attribute]] = ds_value
|
204
|
-
end
|
205
|
-
|
206
|
-
# NBK: need to read the existing password here as it's not actually
|
207
|
-
# stored in the user record. It is stored at a path that involves the
|
208
|
-
# UUID of the user record for non-Mobile local acccounts.
|
209
|
-
# Mobile Accounts are out of scope for this provider for now
|
210
|
-
attribute_hash[:password] = self.get_password(attribute_hash[:guid]) if @resource_type.validproperties.include?(:password) and Puppet.features.root?
|
211
|
-
attribute_hash
|
212
|
-
end
|
213
|
-
|
214
|
-
def self.single_report(resource_name, *type_properties)
|
215
|
-
# JJM 2007-07-24:
|
216
|
-
# Given a the name of an object and a list of properties of that
|
217
|
-
# object, return all property values in a hash.
|
218
|
-
#
|
219
|
-
# This class method returns nil if the object doesn't exist
|
220
|
-
# Otherwise, it returns a hash of the object properties.
|
221
|
-
|
222
|
-
all_present_str_array = list_all_present
|
223
|
-
|
224
|
-
# NBK: shortcut the process if the resource is missing
|
225
|
-
return nil unless all_present_str_array.include? resource_name
|
226
|
-
|
227
|
-
dscl_vector = get_exec_preamble("-read", resource_name)
|
228
|
-
begin
|
229
|
-
dscl_output = execute(dscl_vector)
|
230
|
-
rescue Puppet::ExecutionFailure => detail
|
231
|
-
fail("Could not get report. command execution failed.")
|
232
|
-
end
|
233
|
-
|
234
|
-
# Two code paths is ugly, but until we can drop 10.4 support we don't
|
235
|
-
# have a lot of choice. Ultimately this should all be done using Ruby
|
236
|
-
# to access the DirectoryService APIs directly, but that's simply not
|
237
|
-
# feasible for a while yet.
|
238
|
-
if self.get_macosx_version_major > "10.4"
|
239
|
-
dscl_plist = self.parse_dscl_plist_data(dscl_output)
|
240
|
-
elsif self.get_macosx_version_major == "10.4"
|
241
|
-
dscl_plist = self.parse_dscl_url_data(dscl_output)
|
242
|
-
else
|
243
|
-
fail("Puppet does not support OS X versions < 10.4")
|
244
|
-
end
|
245
|
-
|
246
|
-
self.generate_attribute_hash(dscl_plist, *type_properties)
|
247
|
-
end
|
248
|
-
|
249
|
-
def self.get_exec_preamble(ds_action, resource_name = nil)
|
250
|
-
# JJM 2007-07-24
|
251
|
-
# DSCL commands are often repetitive and contain the same positional
|
252
|
-
# arguments over and over. See http://developer.apple.com/documentation/Porting/Conceptual/PortingUnix/additionalfeatures/chapter_10_section_9.html
|
253
|
-
# for an example of what I mean.
|
254
|
-
# This method spits out proper DSCL commands for us.
|
255
|
-
# We EXPECT name to be @resource[:name] when called from an instance object.
|
256
|
-
|
257
|
-
# 10.4 doesn't support the -plist option for dscl, and 10.5 has a
|
258
|
-
# different format for the -url output with objects with spaces in
|
259
|
-
# their values. *sigh*. Use -url for 10.4 in the hope this can be
|
260
|
-
# deprecated one day, and use -plist for 10.5 and higher.
|
261
|
-
if self.get_macosx_version_major > "10.4"
|
262
|
-
command_vector = [ command(:dscl), "-plist", "." ]
|
263
|
-
elsif self.get_macosx_version_major == "10.4"
|
264
|
-
command_vector = [ command(:dscl), "-url", "." ]
|
265
|
-
else
|
266
|
-
fail("Puppet does not support OS X versions < 10.4")
|
267
|
-
end
|
268
|
-
|
269
|
-
# JJM: The actual action to perform. See "man dscl"
|
270
|
-
# Common actiosn: -create, -delete, -merge, -append, -passwd
|
271
|
-
command_vector << ds_action
|
272
|
-
# JJM: get_ds_path will spit back "Users" or "Groups",
|
273
|
-
# etc... Depending on the Puppet::Type of our self.
|
274
|
-
if resource_name
|
275
|
-
command_vector << "/#{get_ds_path}/#{resource_name}"
|
276
|
-
else
|
277
|
-
command_vector << "/#{get_ds_path}"
|
278
|
-
end
|
279
|
-
# JJM: This returns most of the preamble of the command.
|
280
|
-
# e.g. 'dscl / -create /Users/mccune'
|
281
|
-
command_vector
|
282
|
-
end
|
283
|
-
|
284
|
-
def self.set_password(resource_name, guid, password_hash)
|
285
|
-
password_hash_file = "#{@@password_hash_dir}/#{guid}"
|
286
|
-
begin
|
287
|
-
File.open(password_hash_file, 'w') { |f| f.write(password_hash)}
|
288
|
-
rescue Errno::EACCES => detail
|
289
|
-
fail("Could not write to password hash file: #{detail}")
|
290
|
-
end
|
291
|
-
|
292
|
-
# NBK: For shadow hashes, the user AuthenticationAuthority must contain a value of
|
293
|
-
# ";ShadowHash;". The LKDC in 10.5 makes this more interesting though as it
|
294
|
-
# will dynamically generate ;Kerberosv5;;username@LKDC:SHA1 attributes if
|
295
|
-
# missing. Thus we make sure we only set ;ShadowHash; if it is missing, and
|
296
|
-
# we can do this with the merge command. This allows people to continue to
|
297
|
-
# use other custom AuthenticationAuthority attributes without stomping on them.
|
298
|
-
#
|
299
|
-
# There is a potential problem here in that we're only doing this when setting
|
300
|
-
# the password, and the attribute could get modified at other times while the
|
301
|
-
# hash doesn't change and so this doesn't get called at all... but
|
302
|
-
# without switching all the other attributes to merge instead of create I can't
|
303
|
-
# see a simple enough solution for this that doesn't modify the user record
|
304
|
-
# every single time. This should be a rather rare edge case. (famous last words)
|
305
|
-
|
306
|
-
dscl_vector = self.get_exec_preamble("-merge", resource_name)
|
307
|
-
dscl_vector << "AuthenticationAuthority" << ";ShadowHash;"
|
308
|
-
begin
|
309
|
-
dscl_output = execute(dscl_vector)
|
310
|
-
rescue Puppet::ExecutionFailure => detail
|
311
|
-
fail("Could not set AuthenticationAuthority.")
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
def self.get_password(guid)
|
316
|
-
password_hash = nil
|
317
|
-
password_hash_file = "#{@@password_hash_dir}/#{guid}"
|
318
|
-
if File.exists?(password_hash_file) and File.file?(password_hash_file)
|
319
|
-
fail("Could not read password hash file at #{password_hash_file}") if not File.readable?(password_hash_file)
|
320
|
-
f = File.new(password_hash_file)
|
321
|
-
password_hash = f.read
|
322
|
-
f.close
|
323
|
-
end
|
324
|
-
password_hash
|
325
|
-
end
|
326
|
-
|
327
|
-
def ensure=(ensure_value)
|
328
|
-
super
|
329
|
-
# We need to loop over all valid properties for the type we're
|
330
|
-
# managing and call the method which sets that property value
|
331
|
-
# dscl can't create everything at once unfortunately.
|
332
|
-
if ensure_value == :present
|
333
|
-
@resource.class.validproperties.each do |name|
|
334
|
-
next if name == :ensure
|
335
|
-
# LAK: We use property.sync here rather than directly calling
|
336
|
-
# the settor method because the properties might do some kind
|
337
|
-
# of conversion. In particular, the user gid property might
|
338
|
-
# have a string and need to convert it to a number
|
339
|
-
if @resource.should(name)
|
340
|
-
@resource.property(name).sync
|
341
|
-
elsif value = autogen(name)
|
342
|
-
self.send(name.to_s + "=", value)
|
343
|
-
else
|
344
|
-
next
|
345
|
-
end
|
346
|
-
end
|
347
|
-
end
|
348
|
-
end
|
349
|
-
|
350
|
-
def password=(passphrase)
|
351
|
-
exec_arg_vector = self.class.get_exec_preamble("-read", @resource.name)
|
352
|
-
exec_arg_vector << @@ns_to_ds_attribute_map[:guid]
|
353
|
-
begin
|
354
|
-
guid_output = execute(exec_arg_vector)
|
355
|
-
guid_plist = Plist.parse_xml(guid_output)
|
356
|
-
# Although GeneratedUID like all DirectoryService values can be multi-valued
|
357
|
-
# according to the schema, in practice user accounts cannot have multiple UUIDs
|
358
|
-
# otherwise Bad Things Happen, so we just deal with the first value.
|
359
|
-
guid = guid_plist["dsAttrTypeStandard:#{@@ns_to_ds_attribute_map[:guid]}"][0]
|
360
|
-
self.class.set_password(@resource.name, guid, passphrase)
|
361
|
-
rescue Puppet::ExecutionFailure => detail
|
362
|
-
fail("Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}")
|
363
|
-
end
|
364
|
-
end
|
365
|
-
|
366
|
-
# NBK: we override @parent.set as we need to execute a series of commands
|
367
|
-
# to deal with array values, rather than the single command nameservice.rb
|
368
|
-
# expects to be returned by modifycmd. Thus we don't bother defining modifycmd.
|
369
|
-
|
370
|
-
def set(param, value)
|
371
|
-
self.class.validate(param, value)
|
372
|
-
current_members = @property_value_cache_hash[:members]
|
373
|
-
if param == :members
|
374
|
-
# If we are meant to be authoritative for the group membership
|
375
|
-
# then remove all existing members who haven't been specified
|
376
|
-
# in the manifest.
|
377
|
-
remove_unwanted_members(current_members, value) if @resource[:auth_membership] and not current_members.nil?
|
378
|
-
|
379
|
-
# if they're not a member, make them one.
|
380
|
-
add_members(current_members, value)
|
381
|
-
else
|
382
|
-
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
383
|
-
# JJM: The following line just maps the NS name to the DS name
|
384
|
-
# e.g. { :uid => 'UniqueID' }
|
385
|
-
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(param)]
|
386
|
-
# JJM: The following line sends the actual value to set the property to
|
387
|
-
exec_arg_vector << value.to_s
|
388
|
-
begin
|
389
|
-
execute(exec_arg_vector)
|
390
|
-
rescue Puppet::ExecutionFailure => detail
|
391
|
-
fail("Could not set #{param} on #{@resource.class.name}[#{@resource.name}]: #{detail}")
|
392
|
-
end
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
# NBK: we override @parent.create as we need to execute a series of commands
|
397
|
-
# to create objects with dscl, rather than the single command nameservice.rb
|
398
|
-
# expects to be returned by addcmd. Thus we don't bother defining addcmd.
|
399
|
-
def create
|
400
|
-
if exists?
|
401
|
-
info "already exists"
|
402
|
-
return nil
|
403
|
-
end
|
404
|
-
|
405
|
-
# NBK: First we create the object with a known guid so we can set the contents
|
406
|
-
# of the password hash if required
|
407
|
-
# Shelling out sucks, but for a single use case it doesn't seem worth
|
408
|
-
# requiring people install a UUID library that doesn't come with the system.
|
409
|
-
# This should be revisited if Puppet starts managing UUIDs for other platform
|
410
|
-
# user records.
|
411
|
-
guid = %x{/usr/bin/uuidgen}.chomp
|
412
|
-
|
413
|
-
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
414
|
-
exec_arg_vector << @@ns_to_ds_attribute_map[:guid] << guid
|
415
|
-
begin
|
416
|
-
execute(exec_arg_vector)
|
417
|
-
rescue Puppet::ExecutionFailure => detail
|
418
|
-
fail("Could not set GeneratedUID for #{@resource.class.name} #{@resource.name}: #{detail}")
|
419
|
-
end
|
420
|
-
|
421
|
-
if value = @resource.should(:password) and value != ""
|
422
|
-
self.class.set_password(@resource[:name], guid, value)
|
423
|
-
end
|
424
|
-
|
425
|
-
# Now we create all the standard properties
|
426
|
-
Puppet::Type.type(@resource.class.name).validproperties.each do |property|
|
427
|
-
next if property == :ensure
|
428
|
-
if value = @resource.should(property) and value != ""
|
429
|
-
if property == :members
|
430
|
-
add_members(nil, value)
|
431
|
-
else
|
432
|
-
exec_arg_vector = self.class.get_exec_preamble("-create", @resource[:name])
|
433
|
-
exec_arg_vector << @@ns_to_ds_attribute_map[symbolize(property)]
|
434
|
-
next if property == :password # skip setting the password here
|
435
|
-
exec_arg_vector << value.to_s
|
436
|
-
begin
|
437
|
-
execute(exec_arg_vector)
|
438
|
-
rescue Puppet::ExecutionFailure => detail
|
439
|
-
fail("Could not create #{@resource.class.name} #{@resource.name}: #{detail}")
|
440
|
-
end
|
441
|
-
end
|
442
|
-
end
|
443
|
-
end
|
444
|
-
end
|
445
|
-
|
446
|
-
def remove_unwanted_members(current_members, new_members)
|
447
|
-
current_members.each do |member|
|
448
|
-
if not new_members.flatten.include?(member)
|
449
|
-
cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-d", member, @resource[:name]]
|
450
|
-
begin
|
451
|
-
execute(cmd)
|
452
|
-
rescue Puppet::ExecutionFailure => detail
|
453
|
-
fail("Could not remove #{member} from group: #{@resource.name}, #{detail}")
|
454
|
-
end
|
455
|
-
end
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
def add_members(current_members, new_members)
|
460
|
-
new_members.flatten.each do |new_member|
|
461
|
-
if current_members.nil? or not current_members.include?(new_member)
|
462
|
-
cmd = [:dseditgroup, "-o", "edit", "-n", ".", "-a", new_member, @resource[:name]]
|
463
|
-
begin
|
464
|
-
execute(cmd)
|
465
|
-
rescue Puppet::ExecutionFailure => detail
|
466
|
-
fail("Could not add #{new_member} to group: #{@resource.name}, #{detail}")
|
467
|
-
end
|
468
|
-
end
|
469
|
-
end
|
470
|
-
end
|
471
|
-
|
472
|
-
def deletecmd
|
473
|
-
# JJM: Like addcmd, only called when deleting the object itself
|
474
|
-
# Note, this isn't used to delete properties of the object,
|
475
|
-
# at least that's how I understand it...
|
476
|
-
self.class.get_exec_preamble("-delete", @resource[:name])
|
477
|
-
end
|
478
|
-
|
479
|
-
def getinfo(refresh = false)
|
480
|
-
# JJM 2007-07-24:
|
481
|
-
# Override the getinfo method, which is also defined in nameservice.rb
|
482
|
-
# This method returns and sets @infohash
|
483
|
-
# I'm not re-factoring the name "getinfo" because this method will be
|
484
|
-
# most likely called by nameservice.rb, which I didn't write.
|
485
|
-
if refresh or (! defined?(@property_value_cache_hash) or ! @property_value_cache_hash)
|
486
|
-
# JJM 2007-07-24: OK, there's a bit of magic that's about to
|
487
|
-
# happen... Let's see how strong my grip has become... =)
|
488
|
-
#
|
489
|
-
# self is a provider instance of some Puppet::Type, like
|
490
|
-
# Puppet::Type::User::ProviderDirectoryservice for the case of the
|
491
|
-
# user type and this provider.
|
492
|
-
#
|
493
|
-
# self.class looks like "user provider directoryservice", if that
|
494
|
-
# helps you ...
|
495
|
-
#
|
496
|
-
# self.class.resource_type is a reference to the Puppet::Type class,
|
497
|
-
# probably Puppet::Type::User or Puppet::Type::Group, etc...
|
498
|
-
#
|
499
|
-
# self.class.resource_type.validproperties is a class method,
|
500
|
-
# returning an Array of the valid properties of that specific
|
501
|
-
# Puppet::Type.
|
502
|
-
#
|
503
|
-
# So... something like [:comment, :home, :password, :shell, :uid,
|
504
|
-
# :groups, :ensure, :gid]
|
505
|
-
#
|
506
|
-
# Ultimately, we add :name to the list, delete :ensure from the
|
507
|
-
# list, then report on the remaining list. Pretty whacky, ehh?
|
508
|
-
type_properties = [:name] + self.class.resource_type.validproperties
|
509
|
-
type_properties.delete(:ensure) if type_properties.include? :ensure
|
510
|
-
type_properties << :guid # append GeneratedUID so we just get the report here
|
511
|
-
@property_value_cache_hash = self.class.single_report(@resource[:name], *type_properties)
|
512
|
-
[:uid, :gid].each do |param|
|
513
|
-
@property_value_cache_hash[param] = @property_value_cache_hash[param].to_i if @property_value_cache_hash and @property_value_cache_hash.include?(param)
|
514
|
-
end
|
515
|
-
end
|
516
|
-
@property_value_cache_hash
|
517
|
-
end
|
518
|
-
end
|
519
|
-
end
|