puppet 0.13.6 → 0.16.0
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 +57 -0
- data/Rakefile +38 -410
- data/bin/puppet +14 -12
- data/bin/puppetca +1 -3
- data/bin/puppetd +25 -7
- data/bin/puppetdoc +161 -104
- data/bin/puppetmasterd +4 -4
- data/conf/epm.list +8 -0
- data/conf/redhat/client.init +6 -1
- data/conf/redhat/no-chuser-0.15.1.patch +38 -0
- data/conf/redhat/puppet.spec +20 -5
- data/conf/redhat/puppetd.conf +1 -1
- data/conf/redhat/puppetmasterd.conf +1 -1
- data/conf/redhat/server.init +2 -4
- data/examples/code/snippets/{casestatement → casestatement.pp} +12 -1
- data/examples/code/snippets/selectorvalues.pp +15 -0
- data/examples/code/snippets/singleselector.pp +22 -0
- data/examples/code/snippets/tag.pp +9 -0
- data/ext/module_puppet +1 -1
- data/install.rb +303 -303
- data/lib/puppet.rb +7 -9
- data/lib/puppet/client.rb +18 -5
- data/lib/puppet/client/dipper.rb +12 -10
- data/lib/puppet/client/master.rb +113 -41
- data/lib/puppet/client/pelement.rb +20 -0
- data/lib/puppet/config.rb +113 -6
- data/lib/puppet/element.rb +1 -3
- data/lib/puppet/event.rb +12 -23
- data/lib/puppet/filetype.rb +93 -5
- data/lib/puppet/inifile.rb +201 -0
- data/lib/puppet/log.rb +18 -6
- data/lib/puppet/parameter.rb +80 -29
- data/lib/puppet/parser/ast.rb +6 -4
- data/lib/puppet/parser/ast/caseopt.rb +13 -4
- data/lib/puppet/parser/ast/casestatement.rb +2 -2
- data/lib/puppet/parser/ast/component.rb +4 -14
- data/lib/puppet/parser/ast/hostclass.rb +1 -1
- data/lib/puppet/parser/ast/leaf.rb +12 -0
- data/lib/puppet/parser/ast/node.rb +4 -4
- data/lib/puppet/parser/ast/objectdef.rb +5 -51
- data/lib/puppet/parser/ast/selector.rb +2 -0
- data/lib/puppet/parser/ast/tag.rb +26 -0
- data/lib/puppet/parser/interpreter.rb +89 -74
- data/lib/puppet/parser/lexer.rb +4 -3
- data/lib/puppet/parser/parser.rb +440 -378
- data/lib/puppet/parser/scope.rb +844 -887
- data/lib/puppet/server.rb +12 -1
- data/lib/puppet/server/authconfig.rb +166 -0
- data/lib/puppet/server/authstore.rb +8 -6
- data/lib/puppet/server/ca.rb +23 -26
- data/lib/puppet/server/filebucket.rb +24 -23
- data/lib/puppet/server/fileserver.rb +116 -47
- data/lib/puppet/server/master.rb +58 -19
- data/lib/puppet/server/pelement.rb +176 -0
- data/lib/puppet/server/rights.rb +78 -0
- data/lib/puppet/server/servlet.rb +19 -6
- data/lib/puppet/sslcertificates.rb +4 -2
- data/lib/puppet/sslcertificates/ca.rb +66 -34
- data/lib/puppet/storage.rb +20 -26
- data/lib/puppet/transaction.rb +49 -92
- data/lib/puppet/type.rb +142 -35
- data/lib/puppet/type/cron.rb +29 -14
- data/lib/puppet/type/exec.rb +92 -35
- data/lib/puppet/type/group.rb +29 -11
- data/lib/puppet/type/nameservice.rb +50 -1
- data/lib/puppet/type/nameservice/netinfo.rb +68 -1
- data/lib/puppet/type/nameservice/objectadd.rb +1 -0
- data/lib/puppet/type/package.rb +150 -109
- data/lib/puppet/type/package/apple.rb +27 -0
- data/lib/puppet/type/package/apt.rb +1 -0
- data/lib/puppet/type/package/darwinport.rb +97 -0
- data/lib/puppet/type/package/dpkg.rb +10 -2
- data/lib/puppet/type/package/freebsd.rb +19 -0
- data/lib/puppet/type/package/{bsd.rb → openbsd.rb} +36 -7
- data/lib/puppet/type/package/ports.rb +98 -0
- data/lib/puppet/type/package/rpm.rb +43 -7
- data/lib/puppet/type/package/sun.rb +53 -36
- data/lib/puppet/type/package/yum.rb +5 -16
- data/lib/puppet/type/parsedtype.rb +41 -29
- data/lib/puppet/type/parsedtype/host.rb +13 -5
- data/lib/puppet/type/parsedtype/mount.rb +250 -0
- data/lib/puppet/type/parsedtype/port.rb +8 -6
- data/lib/puppet/type/pfile.rb +284 -30
- data/lib/puppet/type/pfile/checksum.rb +96 -68
- data/lib/puppet/type/pfile/content.rb +16 -13
- data/lib/puppet/type/pfile/ensure.rb +64 -126
- data/lib/puppet/type/pfile/group.rb +12 -5
- data/lib/puppet/type/pfile/mode.rb +16 -4
- data/lib/puppet/type/pfile/source.rb +47 -73
- data/lib/puppet/type/pfile/target.rb +81 -0
- data/lib/puppet/type/pfile/uid.rb +10 -3
- data/lib/puppet/type/pfilebucket.rb +12 -3
- data/lib/puppet/type/schedule.rb +5 -1
- data/lib/puppet/type/service.rb +138 -66
- data/lib/puppet/type/service/debian.rb +9 -3
- data/lib/puppet/type/service/init.rb +51 -56
- data/lib/puppet/type/service/smf.rb +16 -6
- data/lib/puppet/type/state.rb +71 -32
- data/lib/puppet/type/symlink.rb +12 -7
- data/lib/puppet/type/tidy.rb +5 -1
- data/lib/puppet/type/user.rb +116 -20
- data/lib/puppet/type/yumrepo.rb +314 -0
- data/lib/puppet/util.rb +84 -14
- data/test/client/client.rb +41 -18
- data/test/client/master.rb +50 -4
- data/test/executables/puppetbin.rb +31 -4
- data/test/executables/puppetca.rb +18 -2
- data/test/language/ast.rb +201 -31
- data/test/language/interpreter.rb +8 -2
- data/test/{parser → language}/lexer.rb +1 -1
- data/test/language/node.rb +84 -0
- data/test/{parser → language}/parser.rb +1 -1
- data/test/language/scope.rb +101 -2
- data/test/language/snippets.rb +23 -2
- data/test/other/config.rb +99 -1
- data/test/other/filetype.rb +95 -0
- data/test/other/inifile.rb +114 -0
- data/test/other/log.rb +3 -2
- data/test/other/transactions.rb +55 -10
- data/test/puppet/utiltest.rb +25 -1
- data/test/puppettest.rb +140 -46
- data/test/server/authconfig.rb +56 -0
- data/test/server/bucket.rb +32 -18
- data/test/server/fileserver.rb +75 -30
- data/test/server/master.rb +27 -4
- data/test/server/pelement.rb +298 -0
- data/test/server/rights.rb +41 -0
- data/test/server/server.rb +2 -2
- data/test/tagging/tagging.rb +100 -1
- data/test/types/basic.rb +3 -3
- data/test/types/cron.rb +24 -1
- data/test/types/exec.rb +99 -1
- data/test/types/file.rb +298 -2
- data/test/types/filebucket.rb +4 -15
- data/test/types/filesources.rb +43 -14
- data/test/types/group.rb +1 -13
- data/test/types/mount.rb +277 -0
- data/test/types/package.rb +164 -33
- data/test/types/parameter.rb +107 -0
- data/test/types/port.rb +2 -1
- data/test/types/service.rb +37 -2
- data/test/types/state.rb +92 -0
- data/test/types/symlink.rb +30 -2
- data/test/types/tidy.rb +2 -14
- data/test/types/type.rb +35 -1
- data/test/types/user.rb +110 -1
- data/test/types/yumrepo.rb +95 -0
- metadata +316 -290
- data/test/types/filetype.rb +0 -160
data/lib/puppet/config.rb
CHANGED
@@ -95,6 +95,11 @@ class Config
|
|
95
95
|
@used = []
|
96
96
|
end
|
97
97
|
|
98
|
+
# This is mostly just used for testing.
|
99
|
+
def clearused
|
100
|
+
@used = []
|
101
|
+
end
|
102
|
+
|
98
103
|
def symbolize(param)
|
99
104
|
case param
|
100
105
|
when String: return param.intern
|
@@ -174,6 +179,23 @@ class Config
|
|
174
179
|
@created = []
|
175
180
|
end
|
176
181
|
|
182
|
+
# Make a directory with the appropriate user, group, and mode
|
183
|
+
def mkdir(default)
|
184
|
+
obj = nil
|
185
|
+
unless obj = @config[default]
|
186
|
+
raise ArgumentError, "Unknown default %s" % default
|
187
|
+
end
|
188
|
+
|
189
|
+
unless obj.is_a? CFile
|
190
|
+
raise ArgumentError, "Default %s is not a file" % default
|
191
|
+
end
|
192
|
+
|
193
|
+
Puppet::Util.asuser(obj.owner, obj.group) do
|
194
|
+
mode = obj.mode || 0750
|
195
|
+
Dir.mkdir(obj.value, mode)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
177
199
|
# Return all of the parameters associated with a given section.
|
178
200
|
def params(section)
|
179
201
|
section = section.intern if section.is_a? String
|
@@ -222,6 +244,14 @@ class Config
|
|
222
244
|
values[section] = {}
|
223
245
|
end
|
224
246
|
values[section][var.to_s] = value
|
247
|
+
|
248
|
+
# Do some annoying skullduggery here. This is so that
|
249
|
+
# the group can be set in the config file. The problem
|
250
|
+
# is that we're using the word 'group' twice, which is
|
251
|
+
# confusing.
|
252
|
+
if var == :group and section == Puppet.name and @config.include?(:group)
|
253
|
+
@config[:group].value = value
|
254
|
+
end
|
225
255
|
next
|
226
256
|
end
|
227
257
|
|
@@ -263,7 +293,7 @@ class Config
|
|
263
293
|
when String, Integer, Float: # nothing
|
264
294
|
klass = CElement
|
265
295
|
else
|
266
|
-
raise Puppet::Error, "Invalid value '%s' for %s" % [value, hash[:name]]
|
296
|
+
raise Puppet::Error, "Invalid value '%s' for %s" % [value.inspect, hash[:name]]
|
267
297
|
end
|
268
298
|
element = klass.new(hash)
|
269
299
|
element.parent = self
|
@@ -504,6 +534,74 @@ Generated on #{Time.now}.
|
|
504
534
|
@config.has_key?(param)
|
505
535
|
end
|
506
536
|
|
537
|
+
# Open a file with the appropriate user, group, and mode
|
538
|
+
def write(default, *args)
|
539
|
+
obj = nil
|
540
|
+
unless obj = @config[default]
|
541
|
+
raise ArgumentError, "Unknown default %s" % default
|
542
|
+
end
|
543
|
+
|
544
|
+
unless obj.is_a? CFile
|
545
|
+
raise ArgumentError, "Default %s is not a file" % default
|
546
|
+
end
|
547
|
+
|
548
|
+
chown = nil
|
549
|
+
if Process.uid == 0
|
550
|
+
chown = [obj.owner, obj.group]
|
551
|
+
else
|
552
|
+
chown = [nil, nil]
|
553
|
+
end
|
554
|
+
Puppet::Util.asuser(*chown) do
|
555
|
+
mode = obj.mode || 0640
|
556
|
+
|
557
|
+
if args.empty?
|
558
|
+
args << "w"
|
559
|
+
end
|
560
|
+
|
561
|
+
args << mode
|
562
|
+
|
563
|
+
File.open(obj.value, *args) do |file|
|
564
|
+
yield file
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
# Open a non-default file under a default dir with the appropriate user,
|
570
|
+
# group, and mode
|
571
|
+
def writesub(default, file, *args)
|
572
|
+
obj = nil
|
573
|
+
unless obj = @config[default]
|
574
|
+
raise ArgumentError, "Unknown default %s" % default
|
575
|
+
end
|
576
|
+
|
577
|
+
unless obj.is_a? CFile
|
578
|
+
raise ArgumentError, "Default %s is not a file" % default
|
579
|
+
end
|
580
|
+
|
581
|
+
chown = nil
|
582
|
+
if Process.uid == 0
|
583
|
+
chown = [obj.owner, obj.group]
|
584
|
+
else
|
585
|
+
chown = [nil, nil]
|
586
|
+
end
|
587
|
+
|
588
|
+
Puppet::Util.asuser(*chown) do
|
589
|
+
mode = obj.mode || 0640
|
590
|
+
if args.empty?
|
591
|
+
args << "w"
|
592
|
+
end
|
593
|
+
|
594
|
+
args << mode
|
595
|
+
|
596
|
+
# Update the umask to make non-executable files
|
597
|
+
Puppet::Util.withumask(File.umask ^ 0111) do
|
598
|
+
File.open(file, *args) do |file|
|
599
|
+
yield file
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
end
|
604
|
+
|
507
605
|
# The base element type.
|
508
606
|
class CElement
|
509
607
|
attr_accessor :name, :section, :default, :parent, :setbycli
|
@@ -685,15 +783,24 @@ Generated on #{Time.now}.
|
|
685
783
|
if type == :directory or self.create
|
686
784
|
obj[:ensure] = type
|
687
785
|
end
|
688
|
-
[:
|
786
|
+
[:mode].each { |var|
|
689
787
|
if value = self.send(var)
|
690
|
-
obj[var] = value
|
788
|
+
obj[var] = "%o" % value
|
691
789
|
end
|
692
790
|
}
|
693
791
|
|
694
|
-
|
695
|
-
|
792
|
+
# Only chown or chgrp when root
|
793
|
+
if Process.uid == 0
|
794
|
+
[:group, :owner].each { |var|
|
795
|
+
if value = self.send(var)
|
796
|
+
obj[var] = value
|
797
|
+
end
|
798
|
+
}
|
696
799
|
end
|
800
|
+
|
801
|
+
# And set the loglevel to debug for everything
|
802
|
+
obj[:loglevel] = "debug"
|
803
|
+
|
697
804
|
if self.section
|
698
805
|
obj.tags = ["puppet", "configuration", self.section]
|
699
806
|
end
|
@@ -728,4 +835,4 @@ Generated on #{Time.now}.
|
|
728
835
|
end
|
729
836
|
end
|
730
837
|
|
731
|
-
# $Id: config.rb
|
838
|
+
# $Id: config.rb 1111 2006-04-12 17:36:14Z luke $
|
data/lib/puppet/element.rb
CHANGED
@@ -52,8 +52,6 @@ class Puppet::Element
|
|
52
52
|
if self.name == "puppet[top]"
|
53
53
|
@path = ["/"]
|
54
54
|
else
|
55
|
-
# We assume that if we don't have a parent that we should not
|
56
|
-
# cache the path
|
57
55
|
if self.is_a?(Puppet.type(:component))
|
58
56
|
@path = [self.name]
|
59
57
|
else
|
@@ -68,4 +66,4 @@ class Puppet::Element
|
|
68
66
|
|
69
67
|
end
|
70
68
|
|
71
|
-
# $Id: element.rb
|
69
|
+
# $Id: element.rb 980 2006-03-03 23:06:40Z luke $
|
data/lib/puppet/event.rb
CHANGED
@@ -77,12 +77,13 @@ module Puppet
|
|
77
77
|
|
78
78
|
# Trigger the subscriptions related to an event, and then pass it up
|
79
79
|
# as appropriate
|
80
|
-
def self.trigger(source, event
|
80
|
+
def self.trigger(source, event)
|
81
81
|
type, name = self.split(source)
|
82
82
|
|
83
83
|
@subscriptions[type][name].each { |sub|
|
84
84
|
if sub.match?(event)
|
85
|
-
sub
|
85
|
+
yield sub
|
86
|
+
#sub.trigger(transaction)
|
86
87
|
end
|
87
88
|
}
|
88
89
|
end
|
@@ -211,26 +212,14 @@ module Puppet
|
|
211
212
|
target = self.target
|
212
213
|
#Puppet.debug "'%s' matched '%s'; triggering '%s' on '%s'" %
|
213
214
|
# [@source,@event,@method,target]
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
)
|
223
|
-
end
|
224
|
-
rescue => detail
|
225
|
-
# um, what the heck do i do when an object fails to
|
226
|
-
# refresh? shouldn't that result in the transaction
|
227
|
-
# rolling back? the 'onerror' metaparam will be used
|
228
|
-
# to determine behaviour in that case
|
229
|
-
Puppet.err "'%s' failed to %s: '%s'" %
|
230
|
-
[target,@callback,detail]
|
231
|
-
raise
|
232
|
-
#raise "We need to roll '%s' transaction back" %
|
233
|
-
#transaction
|
215
|
+
if target.respond_to?(@callback)
|
216
|
+
target.log "triggering %s" % @callback
|
217
|
+
event = target.send(@callback)
|
218
|
+
else
|
219
|
+
Puppet.debug(
|
220
|
+
"'%s' of type '%s' does not respond to '%s'" %
|
221
|
+
[target,target.class,@callback.inspect]
|
222
|
+
)
|
234
223
|
end
|
235
224
|
transaction.triggered(target, @callback)
|
236
225
|
end
|
@@ -261,4 +250,4 @@ module Puppet
|
|
261
250
|
end
|
262
251
|
end
|
263
252
|
|
264
|
-
# $Id: event.rb
|
253
|
+
# $Id: event.rb 998 2006-03-09 20:46:55Z luke $
|
data/lib/puppet/filetype.rb
CHANGED
@@ -119,19 +119,19 @@ module Puppet
|
|
119
119
|
|
120
120
|
# Read the file.
|
121
121
|
def read
|
122
|
-
Puppet.info "Reading %s" % @path
|
122
|
+
Puppet.info "Reading %s from RAM" % @path
|
123
123
|
@text
|
124
124
|
end
|
125
125
|
|
126
126
|
# Remove the file.
|
127
127
|
def remove
|
128
|
-
Puppet.info "Removing %s" % @path
|
128
|
+
Puppet.info "Removing %s from RAM" % @path
|
129
129
|
@text = ""
|
130
130
|
end
|
131
131
|
|
132
132
|
# Overwrite the file.
|
133
133
|
def write(text)
|
134
|
-
Puppet.info "Writing %s" % @path
|
134
|
+
Puppet.info "Writing %s to RAM" % @path
|
135
135
|
@text = text
|
136
136
|
end
|
137
137
|
end
|
@@ -156,7 +156,9 @@ module Puppet
|
|
156
156
|
raise Puppet::Error, "Could not retrieve user %s" % user
|
157
157
|
end
|
158
158
|
|
159
|
-
|
159
|
+
# We have to have the user name, not the uid, because some
|
160
|
+
# systems *cough*linux*cough* require it that way
|
161
|
+
@path = user
|
160
162
|
end
|
161
163
|
|
162
164
|
# Read a specific @path's cron tab.
|
@@ -205,7 +207,93 @@ module Puppet
|
|
205
207
|
}
|
206
208
|
end
|
207
209
|
end
|
210
|
+
|
211
|
+
# Treat netinfo tables as a single file, just for simplicity of certain types
|
212
|
+
newfiletype(:netinfo) do
|
213
|
+
class << self
|
214
|
+
attr_accessor :format
|
215
|
+
end
|
216
|
+
def read
|
217
|
+
%x{nidump -r /#{@path} /}
|
218
|
+
end
|
219
|
+
|
220
|
+
# This really only makes sense for cron tabs.
|
221
|
+
def remove
|
222
|
+
%x{nireport / /#{@path} name}.split("\n").each do |name|
|
223
|
+
newname = name.gsub(/\//, '\/').sub(/\s+$/, '')
|
224
|
+
output = %x{niutil -destroy / '/#{@path}/#{newname}'}
|
225
|
+
|
226
|
+
unless $? == 0
|
227
|
+
raise Puppet::Error, "Could not remove %s from %s" %
|
228
|
+
[name, @path]
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# Convert our table to an array of hashes. This only works for handling one
|
234
|
+
# table at a time.
|
235
|
+
def to_array(text = nil)
|
236
|
+
unless text
|
237
|
+
text = read
|
238
|
+
end
|
239
|
+
|
240
|
+
hash = nil
|
241
|
+
|
242
|
+
# Initialize it with the first record
|
243
|
+
records = []
|
244
|
+
text.split("\n").each do |line|
|
245
|
+
next if line =~ /^[{}]$/ # Skip the wrapping lines
|
246
|
+
next if line =~ /"name" = \( "#{@path}" \)/ # Skip the table name
|
247
|
+
next if line =~ /CHILDREN = \(/ # Skip this header
|
248
|
+
next if line =~ /^ \)/ # and its closer
|
249
|
+
|
250
|
+
# Now we should have nothing but records, wrapped in braces
|
251
|
+
|
252
|
+
case line
|
253
|
+
when /^\s+\{/: hash = {}
|
254
|
+
when /^\s+\}/: records << hash
|
255
|
+
when /\s+"(\w+)" = \( (.+) \)/
|
256
|
+
field = $1
|
257
|
+
values = $2
|
258
|
+
|
259
|
+
# Always use an array
|
260
|
+
hash[field] = []
|
261
|
+
|
262
|
+
values.split(/, /).each do |value|
|
263
|
+
if value =~ /^"(.*)"$/
|
264
|
+
hash[field] << $1
|
265
|
+
else
|
266
|
+
raise ArgumentError, "Could not match value %s" % value
|
267
|
+
end
|
268
|
+
end
|
269
|
+
else
|
270
|
+
raise ArgumentError, "Could not match line %s" % line
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
records
|
275
|
+
end
|
276
|
+
|
277
|
+
def write(text)
|
278
|
+
text.gsub!(/^#.*\n/,'')
|
279
|
+
text.gsub!(/^$/,'')
|
280
|
+
if text == "" or text == "\n"
|
281
|
+
self.remove
|
282
|
+
return
|
283
|
+
end
|
284
|
+
unless format = self.class.format
|
285
|
+
raise Puppe::DevError, "You must define the NetInfo format to inport"
|
286
|
+
end
|
287
|
+
IO.popen("niload -d #{format} . 1>/dev/null 2>/dev/null", "w") { |p|
|
288
|
+
p.print text
|
289
|
+
}
|
290
|
+
|
291
|
+
unless $? == 0
|
292
|
+
raise ArgumentError, "Failed to write %s" % @path
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
208
296
|
end
|
209
297
|
end
|
210
298
|
|
211
|
-
# $Id: filetype.rb
|
299
|
+
# $Id: filetype.rb 1125 2006-04-20 19:38:48Z luke $
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# Module Puppet::IniConfig
|
2
|
+
# A generic way to parse .ini style files and manipulate them in memory
|
3
|
+
# One 'file' can be made up of several physical files. Changes to sections
|
4
|
+
# on the file are tracked so that only the physical files in which
|
5
|
+
# something has changed are written back to disk
|
6
|
+
# Great care is taken to preserve comments and blank lines from the original
|
7
|
+
# files
|
8
|
+
module Puppet
|
9
|
+
module IniConfig
|
10
|
+
|
11
|
+
# A section in a .ini file
|
12
|
+
class Section
|
13
|
+
attr_reader :name, :file
|
14
|
+
|
15
|
+
def initialize(name, file)
|
16
|
+
@name = name
|
17
|
+
@file = file
|
18
|
+
@dirty = false
|
19
|
+
@entries = []
|
20
|
+
end
|
21
|
+
|
22
|
+
# Has this section been modified since it's been read in
|
23
|
+
# or written back to disk
|
24
|
+
def dirty?
|
25
|
+
@dirty
|
26
|
+
end
|
27
|
+
|
28
|
+
# Should only be used internally
|
29
|
+
def mark_clean
|
30
|
+
@dirty = false
|
31
|
+
end
|
32
|
+
|
33
|
+
# Add a line of text (e.g., a comment) Such lines
|
34
|
+
# will be written back out in exactly the same
|
35
|
+
# place they were read in
|
36
|
+
def add_line(line)
|
37
|
+
@entries << line
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set the entry 'key=value'. If no entry with the
|
41
|
+
# given key exists, one is appended to teh end of the section
|
42
|
+
def []=(key, value)
|
43
|
+
entry = find_entry(key)
|
44
|
+
@dirty = true
|
45
|
+
if entry.nil?
|
46
|
+
@entries << [key, value]
|
47
|
+
else
|
48
|
+
entry[1] = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return the value associated with KEY. If no such entry
|
53
|
+
# exists, return nil
|
54
|
+
def [](key)
|
55
|
+
entry = find_entry(key)
|
56
|
+
if entry.nil?
|
57
|
+
return nil
|
58
|
+
end
|
59
|
+
return entry[1]
|
60
|
+
end
|
61
|
+
|
62
|
+
# Format the section as text in the way it should be
|
63
|
+
# written to file
|
64
|
+
def format
|
65
|
+
text = "[#{name}]\n"
|
66
|
+
@entries.each do |entry|
|
67
|
+
if entry.is_a?(Array)
|
68
|
+
key, value = entry
|
69
|
+
unless value.nil?
|
70
|
+
text << "#{key}=#{value}\n"
|
71
|
+
end
|
72
|
+
else
|
73
|
+
text << entry
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return text
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def find_entry(key)
|
81
|
+
@entries.each do |entry|
|
82
|
+
if entry.is_a?(Array) && entry[0] == key
|
83
|
+
return entry
|
84
|
+
end
|
85
|
+
end
|
86
|
+
return nil
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
# A logical .ini-file that can be spread across several physical
|
92
|
+
# files. For each physical file, call #read with the filename
|
93
|
+
class File
|
94
|
+
def initialize
|
95
|
+
@files = {}
|
96
|
+
end
|
97
|
+
|
98
|
+
# Add the contents of the file with name FILE to the
|
99
|
+
# already existing sections
|
100
|
+
def read(file)
|
101
|
+
text = Puppet::FileType.filetype(:flat).new(file).read
|
102
|
+
if text.nil?
|
103
|
+
raise "Could not find #{file}"
|
104
|
+
end
|
105
|
+
|
106
|
+
section = nil
|
107
|
+
line = 0
|
108
|
+
@files[file] = []
|
109
|
+
text.each_line do |l|
|
110
|
+
line += 1
|
111
|
+
if l =~ /^\[(.+)\]$/
|
112
|
+
section.mark_clean unless section.nil?
|
113
|
+
section = add_section($1, file)
|
114
|
+
elsif l =~ /^(\s*\#|\s*$)/
|
115
|
+
if section.nil?
|
116
|
+
@files[file] << l
|
117
|
+
else
|
118
|
+
section.add_line(l)
|
119
|
+
end
|
120
|
+
elsif l =~ /^\s*(\S+)\s*\=(.+)$/
|
121
|
+
# We allow space around the keys, but not the values
|
122
|
+
# For the values, we don't know if space is significant
|
123
|
+
if section.nil?
|
124
|
+
raise "#{file}:#{line}:Key/value pair outside of a section for key #{$1}"
|
125
|
+
else
|
126
|
+
section[$1] = $2
|
127
|
+
end
|
128
|
+
else
|
129
|
+
# FIXME: We can't deal with continuation lines
|
130
|
+
# that at least yum allows (lines that start with
|
131
|
+
# whitespace, and that should really be appended
|
132
|
+
# to the value of the previous key)
|
133
|
+
raise "#{file}:#{line}: Can't parse '#{l.chomp}'"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
section.mark_clean unless section.nil?
|
137
|
+
end
|
138
|
+
|
139
|
+
# Store all modifications made to sections in this file back
|
140
|
+
# to the physical files. If no modifications were made to
|
141
|
+
# a physical file, nothing is written
|
142
|
+
def store
|
143
|
+
@files.each do |file, lines|
|
144
|
+
text = ""
|
145
|
+
dirty = false
|
146
|
+
lines.each do |l|
|
147
|
+
if l.is_a?(Section)
|
148
|
+
dirty ||= l.dirty?
|
149
|
+
text << l.format
|
150
|
+
l.mark_clean
|
151
|
+
else
|
152
|
+
text << l
|
153
|
+
end
|
154
|
+
end
|
155
|
+
if dirty
|
156
|
+
Puppet::FileType.filetype(:flat).new(file).write(text)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# Execute BLOCK, passing each section in this file
|
162
|
+
# as an argument
|
163
|
+
def each_section(&block)
|
164
|
+
@files.each do |file, list|
|
165
|
+
list.each do |entry|
|
166
|
+
if entry.is_a?(Section)
|
167
|
+
yield(entry)
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
# Return the Section with the given name or nil
|
174
|
+
def [](name)
|
175
|
+
name = name.to_s
|
176
|
+
each_section do |section|
|
177
|
+
return section if section.name == name
|
178
|
+
end
|
179
|
+
return nil
|
180
|
+
end
|
181
|
+
|
182
|
+
# Return true if the file contains a section with name NAME
|
183
|
+
def include?(name)
|
184
|
+
return ! self[name].nil?
|
185
|
+
end
|
186
|
+
|
187
|
+
# Add a section to be stored in FILE when store is called
|
188
|
+
def add_section(name, file)
|
189
|
+
if include?(name)
|
190
|
+
raise "A section with name #{name} already exists"
|
191
|
+
end
|
192
|
+
result = Section.new(name, file)
|
193
|
+
@files[file] ||= []
|
194
|
+
@files[file] << result
|
195
|
+
return result
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
end
|
201
|
+
end
|