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.

Files changed (149) hide show
  1. data/CHANGELOG +57 -0
  2. data/Rakefile +38 -410
  3. data/bin/puppet +14 -12
  4. data/bin/puppetca +1 -3
  5. data/bin/puppetd +25 -7
  6. data/bin/puppetdoc +161 -104
  7. data/bin/puppetmasterd +4 -4
  8. data/conf/epm.list +8 -0
  9. data/conf/redhat/client.init +6 -1
  10. data/conf/redhat/no-chuser-0.15.1.patch +38 -0
  11. data/conf/redhat/puppet.spec +20 -5
  12. data/conf/redhat/puppetd.conf +1 -1
  13. data/conf/redhat/puppetmasterd.conf +1 -1
  14. data/conf/redhat/server.init +2 -4
  15. data/examples/code/snippets/{casestatement → casestatement.pp} +12 -1
  16. data/examples/code/snippets/selectorvalues.pp +15 -0
  17. data/examples/code/snippets/singleselector.pp +22 -0
  18. data/examples/code/snippets/tag.pp +9 -0
  19. data/ext/module_puppet +1 -1
  20. data/install.rb +303 -303
  21. data/lib/puppet.rb +7 -9
  22. data/lib/puppet/client.rb +18 -5
  23. data/lib/puppet/client/dipper.rb +12 -10
  24. data/lib/puppet/client/master.rb +113 -41
  25. data/lib/puppet/client/pelement.rb +20 -0
  26. data/lib/puppet/config.rb +113 -6
  27. data/lib/puppet/element.rb +1 -3
  28. data/lib/puppet/event.rb +12 -23
  29. data/lib/puppet/filetype.rb +93 -5
  30. data/lib/puppet/inifile.rb +201 -0
  31. data/lib/puppet/log.rb +18 -6
  32. data/lib/puppet/parameter.rb +80 -29
  33. data/lib/puppet/parser/ast.rb +6 -4
  34. data/lib/puppet/parser/ast/caseopt.rb +13 -4
  35. data/lib/puppet/parser/ast/casestatement.rb +2 -2
  36. data/lib/puppet/parser/ast/component.rb +4 -14
  37. data/lib/puppet/parser/ast/hostclass.rb +1 -1
  38. data/lib/puppet/parser/ast/leaf.rb +12 -0
  39. data/lib/puppet/parser/ast/node.rb +4 -4
  40. data/lib/puppet/parser/ast/objectdef.rb +5 -51
  41. data/lib/puppet/parser/ast/selector.rb +2 -0
  42. data/lib/puppet/parser/ast/tag.rb +26 -0
  43. data/lib/puppet/parser/interpreter.rb +89 -74
  44. data/lib/puppet/parser/lexer.rb +4 -3
  45. data/lib/puppet/parser/parser.rb +440 -378
  46. data/lib/puppet/parser/scope.rb +844 -887
  47. data/lib/puppet/server.rb +12 -1
  48. data/lib/puppet/server/authconfig.rb +166 -0
  49. data/lib/puppet/server/authstore.rb +8 -6
  50. data/lib/puppet/server/ca.rb +23 -26
  51. data/lib/puppet/server/filebucket.rb +24 -23
  52. data/lib/puppet/server/fileserver.rb +116 -47
  53. data/lib/puppet/server/master.rb +58 -19
  54. data/lib/puppet/server/pelement.rb +176 -0
  55. data/lib/puppet/server/rights.rb +78 -0
  56. data/lib/puppet/server/servlet.rb +19 -6
  57. data/lib/puppet/sslcertificates.rb +4 -2
  58. data/lib/puppet/sslcertificates/ca.rb +66 -34
  59. data/lib/puppet/storage.rb +20 -26
  60. data/lib/puppet/transaction.rb +49 -92
  61. data/lib/puppet/type.rb +142 -35
  62. data/lib/puppet/type/cron.rb +29 -14
  63. data/lib/puppet/type/exec.rb +92 -35
  64. data/lib/puppet/type/group.rb +29 -11
  65. data/lib/puppet/type/nameservice.rb +50 -1
  66. data/lib/puppet/type/nameservice/netinfo.rb +68 -1
  67. data/lib/puppet/type/nameservice/objectadd.rb +1 -0
  68. data/lib/puppet/type/package.rb +150 -109
  69. data/lib/puppet/type/package/apple.rb +27 -0
  70. data/lib/puppet/type/package/apt.rb +1 -0
  71. data/lib/puppet/type/package/darwinport.rb +97 -0
  72. data/lib/puppet/type/package/dpkg.rb +10 -2
  73. data/lib/puppet/type/package/freebsd.rb +19 -0
  74. data/lib/puppet/type/package/{bsd.rb → openbsd.rb} +36 -7
  75. data/lib/puppet/type/package/ports.rb +98 -0
  76. data/lib/puppet/type/package/rpm.rb +43 -7
  77. data/lib/puppet/type/package/sun.rb +53 -36
  78. data/lib/puppet/type/package/yum.rb +5 -16
  79. data/lib/puppet/type/parsedtype.rb +41 -29
  80. data/lib/puppet/type/parsedtype/host.rb +13 -5
  81. data/lib/puppet/type/parsedtype/mount.rb +250 -0
  82. data/lib/puppet/type/parsedtype/port.rb +8 -6
  83. data/lib/puppet/type/pfile.rb +284 -30
  84. data/lib/puppet/type/pfile/checksum.rb +96 -68
  85. data/lib/puppet/type/pfile/content.rb +16 -13
  86. data/lib/puppet/type/pfile/ensure.rb +64 -126
  87. data/lib/puppet/type/pfile/group.rb +12 -5
  88. data/lib/puppet/type/pfile/mode.rb +16 -4
  89. data/lib/puppet/type/pfile/source.rb +47 -73
  90. data/lib/puppet/type/pfile/target.rb +81 -0
  91. data/lib/puppet/type/pfile/uid.rb +10 -3
  92. data/lib/puppet/type/pfilebucket.rb +12 -3
  93. data/lib/puppet/type/schedule.rb +5 -1
  94. data/lib/puppet/type/service.rb +138 -66
  95. data/lib/puppet/type/service/debian.rb +9 -3
  96. data/lib/puppet/type/service/init.rb +51 -56
  97. data/lib/puppet/type/service/smf.rb +16 -6
  98. data/lib/puppet/type/state.rb +71 -32
  99. data/lib/puppet/type/symlink.rb +12 -7
  100. data/lib/puppet/type/tidy.rb +5 -1
  101. data/lib/puppet/type/user.rb +116 -20
  102. data/lib/puppet/type/yumrepo.rb +314 -0
  103. data/lib/puppet/util.rb +84 -14
  104. data/test/client/client.rb +41 -18
  105. data/test/client/master.rb +50 -4
  106. data/test/executables/puppetbin.rb +31 -4
  107. data/test/executables/puppetca.rb +18 -2
  108. data/test/language/ast.rb +201 -31
  109. data/test/language/interpreter.rb +8 -2
  110. data/test/{parser → language}/lexer.rb +1 -1
  111. data/test/language/node.rb +84 -0
  112. data/test/{parser → language}/parser.rb +1 -1
  113. data/test/language/scope.rb +101 -2
  114. data/test/language/snippets.rb +23 -2
  115. data/test/other/config.rb +99 -1
  116. data/test/other/filetype.rb +95 -0
  117. data/test/other/inifile.rb +114 -0
  118. data/test/other/log.rb +3 -2
  119. data/test/other/transactions.rb +55 -10
  120. data/test/puppet/utiltest.rb +25 -1
  121. data/test/puppettest.rb +140 -46
  122. data/test/server/authconfig.rb +56 -0
  123. data/test/server/bucket.rb +32 -18
  124. data/test/server/fileserver.rb +75 -30
  125. data/test/server/master.rb +27 -4
  126. data/test/server/pelement.rb +298 -0
  127. data/test/server/rights.rb +41 -0
  128. data/test/server/server.rb +2 -2
  129. data/test/tagging/tagging.rb +100 -1
  130. data/test/types/basic.rb +3 -3
  131. data/test/types/cron.rb +24 -1
  132. data/test/types/exec.rb +99 -1
  133. data/test/types/file.rb +298 -2
  134. data/test/types/filebucket.rb +4 -15
  135. data/test/types/filesources.rb +43 -14
  136. data/test/types/group.rb +1 -13
  137. data/test/types/mount.rb +277 -0
  138. data/test/types/package.rb +164 -33
  139. data/test/types/parameter.rb +107 -0
  140. data/test/types/port.rb +2 -1
  141. data/test/types/service.rb +37 -2
  142. data/test/types/state.rb +92 -0
  143. data/test/types/symlink.rb +30 -2
  144. data/test/types/tidy.rb +2 -14
  145. data/test/types/type.rb +35 -1
  146. data/test/types/user.rb +110 -1
  147. data/test/types/yumrepo.rb +95 -0
  148. metadata +316 -290
  149. data/test/types/filetype.rb +0 -160
@@ -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
- [:group, :mode].each { |var|
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
- if Process.uid == 0 and value = self.send(:owner)
695
- obj[:owner] = value
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 967 2006-03-02 20:28:45Z luke $
838
+ # $Id: config.rb 1111 2006-04-12 17:36:14Z luke $
@@ -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 787 2006-01-08 00:02:23Z luke $
69
+ # $Id: element.rb 980 2006-03-03 23:06:40Z luke $
@@ -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, transaction)
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.trigger(transaction)
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
- begin
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
- )
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 965 2006-03-02 07:30:14Z luke $
253
+ # $Id: event.rb 998 2006-03-09 20:46:55Z luke $
@@ -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
- @path = uid
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 910 2006-02-15 07:20:36Z luke $
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