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
@@ -1,1046 +1,1003 @@
1
1
  # The scope class, which handles storing and retrieving variables and types and
2
2
  # such.
3
3
 
4
+ require 'puppet/parser/parser'
4
5
  require 'puppet/transportable'
5
6
 
6
- module Puppet
7
- module Parser
8
- class Scope
9
- class ScopeObj < Hash
10
- attr_accessor :file, :line, :type, :name
11
- end
7
+ module Puppet::Parser
8
+ class Scope
9
+ class ScopeObj < Hash
10
+ attr_accessor :file, :line, :type, :name
11
+ end
12
12
 
13
- Puppet::Util.logmethods(self)
13
+ Puppet::Util.logmethods(self)
14
14
 
15
- include Enumerable
16
- attr_accessor :parent, :level, :interp
17
- attr_accessor :name, :type, :topscope, :base, :keyword
15
+ include Enumerable
16
+ attr_accessor :parent, :level, :interp
17
+ attr_accessor :name, :type, :topscope, :base, :keyword
18
18
 
19
- attr_accessor :top, :context
19
+ attr_accessor :top, :context
20
20
 
21
- # This is probably not all that good of an idea, but...
22
- # This way a parent can share its tables with all of its children.
23
- attr_writer :nodetable, :classtable, :definedtable
21
+ # This is probably not all that good of an idea, but...
22
+ # This way a parent can share its tables with all of its children.
23
+ attr_writer :nodetable, :classtable, :definedtable
24
24
 
25
- # Whether we behave declaratively. Note that it's a class variable,
26
- # so all scopes behave the same.
27
- @@declarative = true
25
+ # Whether we behave declaratively. Note that it's a class variable,
26
+ # so all scopes behave the same.
27
+ @@declarative = true
28
28
 
29
- # Retrieve and set the declarative setting.
30
- def Scope.declarative
31
- return @@declarative
32
- end
29
+ # Retrieve and set the declarative setting.
30
+ def self.declarative
31
+ return @@declarative
32
+ end
33
33
 
34
- def Scope.declarative=(val)
35
- @@declarative = val
36
- end
34
+ def self.declarative=(val)
35
+ @@declarative = val
36
+ end
37
+
38
+ # Add all of the defaults for a given object to that object.
39
+ def adddefaults(obj)
40
+ defaults = lookupdefaults(obj.type)
37
41
 
38
- # Add a single object's tags to the global list of tags for
39
- # that object.
40
- def addtags(obj)
41
- unless defined? @tagtable
42
- raise Puppet::DevError, "Told to add tags, but no tag table"
42
+ defaults.each do |var, value|
43
+ unless obj[var]
44
+ self.debug "Adding default %s for %s" %
45
+ [var, obj.type]
46
+
47
+ obj[var] = value
43
48
  end
44
- list = @tagtable[obj.type][obj.name]
45
-
46
- obj.tags.each { |tag|
47
- unless list.include?(tag)
48
- if tag.nil? or tag == ""
49
- Puppet.debug "Got tag %s from %s(%s)" %
50
- [tag.inspect, obj.type, obj.name]
51
- else
52
- list << tag
53
- end
54
- end
55
- }
56
49
  end
50
+ end
57
51
 
58
- # Is the type a builtin type?
59
- def builtintype?(type)
60
- if typeklass = Puppet::Type.type(type)
61
- return typeklass
62
- else
63
- return false
64
- end
52
+ # Add a single object's tags to the global list of tags for
53
+ # that object.
54
+ def addtags(obj)
55
+ unless defined? @tagtable
56
+ raise Puppet::DevError, "Told to add tags, but no tag table"
65
57
  end
58
+ list = @tagtable[obj.type][obj.name]
66
59
 
67
- # Verify that the given object isn't defined elsewhere.
68
- def chkobjectclosure(hash)
69
- type = hash[:type]
70
- name = hash[:name]
71
- unless name
72
- return true
73
- end
74
- if @definedtable[type].include?(name)
75
- typeklass = Puppet::Type.type(type)
76
- if typeklass and ! typeklass.isomorphic?
77
- Puppet.info "Allowing duplicate %s" % type
60
+ obj.tags.each { |tag|
61
+ unless list.include?(tag)
62
+ if tag.nil? or tag == ""
63
+ Puppet.debug "Got tag %s from %s(%s)" %
64
+ [tag.inspect, obj.type, obj.name]
78
65
  else
79
- # Either it's a defined type, which are never
80
- # isomorphic, or it's a non-isomorphic type.
81
- msg = "Duplicate definition: %s[%s] is already defined" %
82
- [type, name]
83
- error = Puppet::ParseError.new(msg)
84
- if hash[:line]
85
- error.line = hash[:line]
86
- end
87
- if hash[:file]
88
- error.file = hash[:file]
89
- end
90
- raise error
66
+ list << tag
91
67
  end
92
68
  end
69
+ }
70
+ end
93
71
 
94
- return true
95
- end
96
-
97
- def declarative=(val)
98
- self.class.declarative = val
99
- end
100
-
101
- def declarative
102
- self.class.declarative
72
+ # Is the type a builtin type?
73
+ def builtintype?(type)
74
+ if typeklass = Puppet::Type.type(type)
75
+ return typeklass
76
+ else
77
+ return false
103
78
  end
79
+ end
104
80
 
105
- # Log the existing tags. At some point this should be in a better
106
- # place, but eh.
107
- def logtags
108
- @tagtable.sort { |a, b|
109
- a[0] <=> b[0]
110
- }.each { |type, names|
111
- names.sort { |a, b|
112
- a[0] <=> b[0]
113
- }.each { |name, tags|
114
- Puppet.info "%s(%s): '%s'" % [type, name, tags.join("' '")]
115
- }
116
- }
81
+ # Verify that the given object isn't defined elsewhere.
82
+ def chkobjectclosure(hash)
83
+ type = hash[:type]
84
+ name = hash[:name]
85
+ unless name
86
+ return true
117
87
  end
118
-
119
- # Create a new child scope.
120
- def child=(scope)
121
- @children.push(scope)
122
-
123
- if defined? @nodetable
124
- scope.nodetable = @nodetable
125
- else
126
- raise Puppet::DevError, "No nodetable has been defined"
127
- end
128
-
129
- if defined? @classtable
130
- scope.classtable = @classtable
88
+ if @definedtable[type].include?(name)
89
+ typeklass = Puppet::Type.type(type)
90
+ if typeklass and ! typeklass.isomorphic?
91
+ Puppet.info "Allowing duplicate %s" % type
131
92
  else
132
- raise Puppet::DevError, "No classtable has been defined"
133
- end
134
-
135
- if defined? @definedtable
136
- scope.definedtable = @definedtable
137
- else
138
- raise Puppet::DevError, "No definedtable has been defined"
139
- end
140
- end
141
-
142
- # Test whether a given scope is declarative. Even though it's
143
- # a global value, the calling objects don't need to know that.
144
- def declarative?
145
- @@declarative
146
- end
147
-
148
- # Remove a specific child.
149
- def delete(child)
150
- @children.delete(child)
151
- end
152
-
153
- # Verify that no nodescopes are hanging around.
154
- def nodeclean
155
- @children.find_all { |child|
156
- if child.is_a?(Scope)
157
- child.nodescope?
158
- else
159
- false
93
+ # Either it's a defined type, which are never
94
+ # isomorphic, or it's a non-isomorphic type.
95
+ msg = "Duplicate definition: %s[%s] is already defined" %
96
+ [type, name]
97
+ error = Puppet::ParseError.new(msg)
98
+ if hash[:line]
99
+ error.line = hash[:line]
160
100
  end
161
- }.each { |child|
162
- @children.delete(child)
163
- }
164
-
165
- @children.each { |child|
166
- if child.is_a?(Scope)
167
- child.nodeclean
101
+ if hash[:file]
102
+ error.file = hash[:file]
168
103
  end
169
- }
104
+ raise error
105
+ end
170
106
  end
171
107
 
172
- # Is this scope associated with being a node? The answer determines
173
- # whether we store class instances here
174
- def nodescope?
175
- @nodescope
176
- end
108
+ return true
109
+ end
177
110
 
178
- # Mark that we are a nodescope.
179
- def isnodescope
180
- @nodescope = true
111
+ def declarative=(val)
112
+ self.class.declarative = val
113
+ end
181
114
 
182
- # Also, create the extra tables associated with being a node
183
- # scope.
184
- # The table for storing class singletons.
185
- @classtable = Hash.new(nil)
115
+ def declarative
116
+ self.class.declarative
117
+ end
186
118
 
187
- # Also, create the object checking map
188
- @definedtable = Hash.new { |types, type|
189
- types[type] = {}
119
+ # Log the existing tags. At some point this should be in a better
120
+ # place, but eh.
121
+ def logtags
122
+ @tagtable.sort { |a, b|
123
+ a[0] <=> b[0]
124
+ }.each { |type, names|
125
+ names.sort { |a, b|
126
+ a[0] <=> b[0]
127
+ }.each { |name, tags|
128
+ Puppet.info "%s(%s): '%s'" % [type, name, tags.join("' '")]
190
129
  }
191
- end
130
+ }
131
+ end
192
132
 
193
- # Are we the top scope?
194
- def topscope?
195
- @level == 1
196
- end
133
+ # Create a new child scope.
134
+ def child=(scope)
135
+ @children.push(scope)
197
136
 
198
- # Return a list of all of the defined classes.
199
- def classlist
200
- unless defined? @classtable
201
- raise Puppet::DevError, "Scope did not receive class table"
202
- end
203
- return @classtable.keys
137
+ if defined? @nodetable
138
+ scope.nodetable = @nodetable
139
+ else
140
+ raise Puppet::DevError, "No nodetable has been defined"
204
141
  end
205
142
 
206
- # Yield each child scope in turn
207
- def each
208
- @children.reject { |child|
209
- yield child
210
- }
143
+ if defined? @classtable
144
+ scope.classtable = @classtable
145
+ else
146
+ raise Puppet::DevError, "No classtable has been defined"
211
147
  end
212
148
 
213
- # Evaluate a specific node's code. This method will normally be called
214
- # on the top-level scope, but it actually evaluates the node at the
215
- # appropriate scope.
216
- #def evalnode(names, facts, classes = nil, parent = nil)
217
- def evalnode(hash)
218
- names = hash[:name]
219
- facts = hash[:facts]
220
- classes = hash[:classes]
221
- parent = hash[:parent]
222
- # First make sure there aren't any other node scopes lying around
223
- self.nodeclean
224
-
225
- # If they've passed classes in, then just generate from there.
226
- if classes
227
- return self.gennode(
228
- :names => names,
229
- :facts => facts,
230
- :classes => classes,
231
- :parent => parent
232
- )
233
- end
234
-
235
- scope = code = nil
236
- # Find a node that matches one of our names
237
- names.each { |node|
238
- if hash = @nodetable[node]
239
- code = hash[:node]
240
- scope = hash[:scope]
241
- break
242
- end
243
- }
244
-
245
- # And fail if we don't find one.
246
- unless scope and code
247
- raise Puppet::Error, "Could not find configuration for %s" %
248
- names.join(" or ")
249
- end
250
-
251
- # We need to do a little skullduggery here. We want a
252
- # temporary scope, because we don't want this scope to
253
- # show up permanently in the scope tree -- otherwise we could
254
- # not evaluate the node multiple times. We could conceivably
255
- # cache the results, but it's not worth it at this stage.
256
-
257
- # Note that we evaluate the node code with its containing
258
- # scope, not with the top scope. We also retrieve the created
259
- # nodescope so that we can get any classes set within it
260
- nodescope = code.safeevaluate(:scope => scope, :facts => facts)
261
-
262
- # We don't need to worry about removing the Node code because
263
- # it will be removed during translation.
264
-
265
- # convert the whole thing
266
- objects = self.to_trans
267
-
268
- # Add any evaluated classes to our top-level object
269
- unless nodescope.classlist.empty?
270
- objects.classes = nodescope.classlist
271
- end
272
-
273
- if objects.is_a?(Puppet::TransBucket)
274
- objects.top = true
275
- end
276
- # I should do something to add the node as an object with tags
277
- # but that will possibly end up with far too many tags.
278
- #self.logtags
279
- return objects
149
+ if defined? @definedtable
150
+ scope.definedtable = @definedtable
151
+ else
152
+ raise Puppet::DevError, "No definedtable has been defined"
280
153
  end
154
+ end
281
155
 
282
- # Pull in all of the appropriate classes and evaluate them. It'd
283
- # be nice if this didn't know quite so much about how AST::Node
284
- # operated internally.
285
- #def gennode(names, facts, classes, parent)
286
- def gennode(hash)
287
- names = hash[:names]
288
- facts = hash[:facts]
289
- classes = hash[:classes]
290
- parent = hash[:parent]
291
- name = names.shift
292
- arghash = {
293
- :type => name,
294
- :code => AST::ASTArray.new(:pin => "[]")
295
- }
296
-
297
- if parent
298
- arghash[:parentclass] = parent
299
- end
156
+ # Test whether a given scope is declarative. Even though it's
157
+ # a global value, the calling objects don't need to know that.
158
+ def declarative?
159
+ @@declarative
160
+ end
300
161
 
301
- # Create the node
302
- node = AST::Node.new(arghash)
303
- node.keyword = "node"
304
-
305
- # Now evaluate it, which evaluates the parent but doesn't really
306
- # do anything else but does return the nodescope
307
- scope = node.safeevaluate(:scope => self)
308
-
309
- # And now evaluate each set klass within the nodescope.
310
- classes.each { |klass|
311
- if code = scope.lookuptype(klass)
312
- #code.safeevaluate(scope, {}, klass, klass)
313
- code.safeevaluate(
314
- :scope => scope,
315
- :facts => {},
316
- :type => klass
317
- )
318
- end
319
- }
162
+ # Remove a specific child.
163
+ def delete(child)
164
+ @children.delete(child)
165
+ end
320
166
 
321
- return scope.to_trans
322
- end
167
+ # Are we the top scope?
168
+ def topscope?
169
+ @level == 1
170
+ end
323
171
 
324
- # Retrieve a specific node. This is used in ast.rb to find a
325
- # parent node and in findnode to retrieve and evaluate a node.
326
- def node(name)
327
- @nodetable[name]
172
+ # Return a list of all of the defined classes.
173
+ def classlist
174
+ unless defined? @classtable
175
+ raise Puppet::DevError, "Scope did not receive class table"
328
176
  end
177
+ return @classtable.values
178
+ end
329
179
 
330
- # Store a host in the site node table.
331
- def setnode(name,code)
332
- unless defined? @nodetable
333
- raise Puppet::DevError, "No node table defined"
334
- end
335
- if @nodetable.include?(name)
336
- raise Puppet::ParseError, "Host %s is already defined" % name
337
- else
338
- #Puppet.warning "Setting node %s at level %s" % [name, @level]
180
+ # Yield each child scope in turn
181
+ def each
182
+ @children.reject { |child|
183
+ yield child
184
+ }
185
+ end
339
186
 
340
- # We have to store both the scope that's setting the node and
341
- # the node itself, so that the node gets evaluated in the correct
342
- # scope.
343
- @nodetable[name] = {
187
+ # Evaluate a list of classes.
188
+ def evalclasses(classes)
189
+ return unless classes
190
+ classes.each do |klass|
191
+ if code = lookuptype(klass)
192
+ code.safeevaluate(
344
193
  :scope => self,
345
- :node => code
346
- }
194
+ :facts => {},
195
+ :type => klass
196
+ )
347
197
  end
348
198
  end
199
+ end
349
200
 
350
- # Evaluate normally, with no node definitions. This is a bit of a
351
- # silly method, in that it just calls evaluate on the passed-in
352
- # objects, and then calls to_trans on itself. It just conceals
353
- # a paltry amount of info from whomever's using the scope object.
354
- def evaluate(hash)
355
- objects = hash[:ast]
356
- facts = hash[:facts] || {}
357
- classes = hash[:classes] || []
358
- facts.each { |var, value|
359
- self.setvar(var, value)
360
- }
201
+ # Evaluate a specific node's code. This method will normally be called
202
+ # on the top-level scope, but it actually evaluates the node at the
203
+ # appropriate scope.
204
+ #def evalnode(names, facts, classes = nil, parent = nil)
205
+ def evalnode(hash)
206
+ objects = hash[:ast]
207
+ names = hash[:names] or
208
+ raise Puppet::DevError, "Node names must be provided to evalnode"
209
+ facts = hash[:facts]
210
+ classes = hash[:classes]
211
+ parent = hash[:parent]
212
+
213
+ scope = code = nil
214
+ # Find a node that matches one of our names
215
+ names.each { |node|
216
+ if nodehash = @nodetable[node]
217
+ code = nodehash[:node]
218
+ scope = nodehash[:scope]
219
+ break
220
+ end
221
+ }
222
+
223
+ # And fail if we don't find one.
224
+ unless scope and code
225
+ raise Puppet::Error, "Could not find configuration for %s" %
226
+ names.join(" or ")
227
+ end
228
+
229
+ # We need to do a little skullduggery here. We want a
230
+ # temporary scope, because we don't want this scope to
231
+ # show up permanently in the scope tree -- otherwise we could
232
+ # not evaluate the node multiple times. We could conceivably
233
+ # cache the results, but it's not worth it at this stage.
234
+
235
+ # Note that we evaluate the node code with its containing
236
+ # scope, not with the top scope. We also retrieve the created
237
+ # scope so that we can get any classes set within it
238
+ nodescope = code.safeevaluate(:scope => scope, :facts => facts)
239
+
240
+ scope.evalclasses(classes)
241
+ end
361
242
 
362
- objects.safeevaluate(:scope => self)
243
+ # Evaluate normally, with no node definitions. This is a bit of a
244
+ # silly method, in that it just calls evaluate on the passed-in
245
+ # objects, and then calls to_trans on itself. It just conceals
246
+ # a paltry amount of info from whomever's using the scope object.
247
+ def evaluate(hash)
248
+ objects = hash[:ast]
249
+ facts = hash[:facts] || {}
250
+
251
+ unless objects
252
+ raise Puppet::DevError, "Evaluation requires an AST tree"
253
+ end
254
+ # Set all of our facts in the top-level scope.
255
+ facts.each { |var, value|
256
+ self.setvar(var, value)
257
+ }
258
+
259
+ # Evaluate all of our configuration. This does not evaluate any
260
+ # node definitions.
261
+ objects.safeevaluate(:scope => self)
262
+
263
+ # If they've provided a name or a parent, we assume they're looking for nodes.
264
+ if hash.include? :parentnode
265
+ # Specifying a parent node takes precedence, because it is assumed
266
+ # that this node was found in a remote repository like ldap.
267
+ gennode(hash)
268
+ elsif hash.include? :names # else, look for it in the config
269
+ evalnode(hash)
270
+ else
271
+ # Else we're not using nodes at all, so just evaluate any passed-in
272
+ # classes.
273
+ classes = hash[:classes] || []
274
+ evalclasses(classes)
363
275
 
364
276
  # These classes would be passed in manually, via something like
365
277
  # a cfengine module
366
- classes.each { |klass|
367
- if code = self.lookuptype(klass)
368
- code.safeevaluate(
369
- :scope => self,
370
- :facts => {},
371
- :type => klass
372
- )
373
- end
374
- }
375
-
376
- objects = self.to_trans
377
- objects.top = true
378
-
379
- # Add our class list
380
- unless self.classlist.empty?
381
- objects.classes = self.classlist
382
- end
383
-
384
- return objects
385
278
  end
386
279
 
387
- # Take all of our objects and evaluate them.
388
- def finish
389
- self.info "finishing"
390
- @objectlist.each { |object|
391
- if object.is_a? ScopeObj
392
- self.info "finishing %s" % object.name
393
- if obj = finishobject(object)
394
- @children << obj
395
- end
396
- end
397
- }
398
-
399
- @finished = true
400
-
401
- self.info "finished"
402
- end
403
-
404
- # If the object is defined in an upper scope, then add our
405
- # params to that upper scope; else, create a transobject
406
- # or evaluate the definition.
407
- def finishobject(object)
408
- type = object.type
409
- name = object.name
410
-
411
- # It should be a defined type.
412
- definedtype = self.lookuptype(type)
413
-
414
- unless definedtype
415
- error = Puppet::ParseError.new("No such type %s" % type)
416
- error.line = object.line
417
- error.file = object.file
418
- raise error
419
- end
420
-
421
- return definedtype.safeevaluate(
422
- :scope => self,
423
- :arguments => object,
424
- :type => type,
425
- :name => name
426
- )
427
- end
280
+ objects = self.to_trans
281
+ objects.top = true
428
282
 
429
- def finished?
430
- @finished
283
+ # Add our class list
284
+ unless self.classlist.empty?
285
+ objects.classes = self.classlist
431
286
  end
432
287
 
433
- # Initialize our new scope. Defaults to having no parent and to
434
- # being declarative.
435
- def initialize(hash = {})
436
- @parent = nil
437
- @type = nil
438
- @name = nil
439
- @finished = false
440
- hash.each { |name, val|
441
- method = name.to_s + "="
442
- if self.respond_to? method
443
- self.send(method, val)
444
- else
445
- raise Puppet::DevError, "Invalid scope argument %s" % name
446
- end
447
- }
448
- #@parent = hash[:parent]
449
- @nodescope = false
288
+ return objects
289
+ end
450
290
 
451
- @tags = []
291
+ # Pull in all of the appropriate classes and evaluate them. It'd
292
+ # be nice if this didn't know quite so much about how AST::Node
293
+ # operated internally. This is used when a list of classes is passed in,
294
+ # instead of a node definition, such as from the cfengine module.
295
+ def gennode(hash)
296
+ names = hash[:names] or
297
+ raise Puppet::DevError, "Node names must be provided to gennode"
298
+ facts = hash[:facts]
299
+ classes = hash[:classes]
300
+ parent = hash[:parentnode]
301
+ name = names.shift
302
+ arghash = {
303
+ :type => name,
304
+ :code => AST::ASTArray.new(:pin => "[]")
305
+ }
306
+
307
+ #Puppet.notice "hash is %s" %
308
+ # hash.inspect
309
+ Puppet.notice "Classes are %s, parent is %s" %
310
+ [classes.inspect, parent.inspect]
311
+
312
+ if parent
313
+ arghash[:parentclass] = parent
314
+ end
315
+
316
+ # Create the node
317
+ node = AST::Node.new(arghash)
318
+ node.keyword = "node"
319
+
320
+ # Now evaluate it, which evaluates the parent and nothing else
321
+ # but does return the nodescope.
322
+ scope = node.safeevaluate(:scope => self)
323
+
324
+ # Finally evaluate our list of classes in this new scope.
325
+ scope.evalclasses(classes)
326
+ end
452
327
 
453
- if @parent.nil?
454
- unless hash.include?(:declarative)
455
- hash[:declarative] = true
456
- end
457
- self.istop(hash[:declarative])
328
+ # Take all of our objects and evaluate them.
329
+ # def finish
330
+ # self.info "finishing"
331
+ # @objectlist.each { |object|
332
+ # if object.is_a? ScopeObj
333
+ # self.info "finishing %s" % object.name
334
+ # if obj = finishobject(object)
335
+ # @children << obj
336
+ # end
337
+ # end
338
+ # }
339
+ #
340
+ # @finished = true
341
+ #
342
+ # self.info "finished"
343
+ # end
344
+ #
345
+ # # If the object is defined in an upper scope, then add our
346
+ # # params to that upper scope; else, create a transobject
347
+ # # or evaluate the definition.
348
+ # def finishobject(object)
349
+ # type = object.type
350
+ # name = object.name
351
+ #
352
+ # # It should be a defined type.
353
+ # definedtype = lookuptype(type)
354
+ #
355
+ # unless definedtype
356
+ # error = Puppet::ParseError.new("No such type %s" % type)
357
+ # error.line = object.line
358
+ # error.file = object.file
359
+ # raise error
360
+ # end
361
+ #
362
+ # return definedtype.safeevaluate(
363
+ # :scope => self,
364
+ # :arguments => object,
365
+ # :type => type,
366
+ # :name => name
367
+ # )
368
+ # end
369
+ #
370
+ # def finished?
371
+ # @finished
372
+ # end
373
+
374
+ # Initialize our new scope. Defaults to having no parent and to
375
+ # being declarative.
376
+ def initialize(hash = {})
377
+ @parent = nil
378
+ @type = nil
379
+ @name = nil
380
+ @finished = false
381
+ hash.each { |name, val|
382
+ method = name.to_s + "="
383
+ if self.respond_to? method
384
+ self.send(method, val)
458
385
  else
459
- @parent.child = self
460
- @level = @parent.level + 1
461
- @interp = @parent.interp
462
- @topscope = @parent.topscope
463
- @context = @parent.context
464
- end
386
+ raise Puppet::DevError, "Invalid scope argument %s" % name
387
+ end
388
+ }
389
+
390
+ @tags = []
391
+
392
+ if @parent.nil?
393
+ unless hash.include?(:declarative)
394
+ hash[:declarative] = true
395
+ end
396
+ self.istop(hash[:declarative])
397
+ else
398
+ @parent.child = self
399
+ @level = @parent.level + 1
400
+ @interp = @parent.interp
401
+ @topscope = @parent.topscope
402
+ @context = @parent.context
403
+ end
404
+
405
+ # Our child scopes and objects
406
+ @children = []
407
+
408
+ # The symbol table for this scope
409
+ @symtable = Hash.new(nil)
410
+
411
+ # The type table for this scope
412
+ @typetable = Hash.new(nil)
413
+
414
+ # All of the defaults set for types. It's a hash of hashes,
415
+ # with the first key being the type, then the second key being
416
+ # the parameter.
417
+ @defaultstable = Hash.new { |dhash,type|
418
+ dhash[type] = Hash.new(nil)
419
+ }
420
+
421
+ # The object table is similar, but it is actually a hash of hashes
422
+ # where the innermost objects are TransObject instances.
423
+ @objectable = Hash.new { |typehash,typekey|
424
+ # See #newobject for how to create the actual objects
425
+ typehash[typekey] = Hash.new(nil)
426
+ }
427
+
428
+ # The list of simpler hash objects.
429
+ @objectlist = []
430
+
431
+ # This is just for collecting statements locally, so we can
432
+ # verify that there is no overlap within this specific scope
433
+ @localobjectable = Hash.new { |typehash,typekey|
434
+ typehash[typekey] = Hash.new(nil)
435
+ }
436
+
437
+ # Map the names to the tables.
438
+ @map = {
439
+ "variable" => @symtable,
440
+ "type" => @typetable,
441
+ "node" => @nodetable,
442
+ "object" => @objectable,
443
+ "defaults" => @defaultstable
444
+ }
445
+ end
465
446
 
466
- # Our child scopes and objects
467
- @children = []
447
+ # Mark that we're the top scope, and set some hard-coded info.
448
+ def istop(declarative = true)
449
+ # the level is mostly used for debugging
450
+ @level = 1
468
451
 
469
- # The symbol table for this scope
470
- @symtable = Hash.new(nil)
452
+ # The table for storing class singletons. This will only actually
453
+ # be used by top scopes and node scopes.
454
+ @classtable = Hash.new(nil)
471
455
 
472
- # The type table for this scope
473
- @typetable = Hash.new(nil)
456
+ self.class.declarative = declarative
474
457
 
475
- # All of the defaults set for types. It's a hash of hashes,
476
- # with the first key being the type, then the second key being
477
- # the parameter.
478
- @defaultstable = Hash.new { |dhash,type|
479
- dhash[type] = Hash.new(nil)
480
- }
458
+ # The table for all defined objects.
459
+ @definedtable = Hash.new { |types, type|
460
+ types[type] = {}
461
+ }
481
462
 
482
- # The object table is similar, but it is actually a hash of hashes
483
- # where the innermost objects are TransObject instances.
484
- @objectable = Hash.new { |typehash,typekey|
485
- # See #newobject for how to create the actual objects
486
- typehash[typekey] = Hash.new(nil)
487
- }
488
-
489
- # The list of simpler hash objects.
490
- @objectlist = []
463
+ # A table for storing nodes.
464
+ @nodetable = Hash.new(nil)
491
465
 
492
- # This is just for collecting statements locally, so we can
493
- # verify that there is no overlap within this specific scope
494
- @localobjectable = Hash.new { |typehash,typekey|
495
- typehash[typekey] = Hash.new(nil)
496
- }
466
+ # Eventually, if we support sites, this will allow definitions
467
+ # of nodes with the same name in different sites. For now
468
+ # the top-level scope is always the only site scope.
469
+ @sitescope = true
497
470
 
498
- # Map the names to the tables.
499
- @map = {
500
- "variable" => @symtable,
501
- "type" => @typetable,
502
- "node" => @nodetable,
503
- "object" => @objectable,
504
- "defaults" => @defaultstable
471
+ # And create a tag table, so we can collect all of the tags
472
+ # associated with any objects created in this scope tree
473
+ @tagtable = Hash.new { |types, type|
474
+ types[type] = Hash.new { |names, name|
475
+ names[name] = []
505
476
  }
506
- end
477
+ }
507
478
 
508
- # Mark that we're the top scope, and set some hard-coded info.
509
- def istop(declarative = true)
510
- # the level is mostly used for debugging
511
- @level = 1
512
-
513
- # The table for storing class singletons. This will only actually
514
- # be used by top scopes and node scopes.
515
- @classtable = Hash.new(nil)
479
+ @context = nil
480
+ @topscope = self
481
+ @type = "puppet"
482
+ @name = "top"
483
+ end
516
484
 
517
- self.class.declarative = declarative
485
+ # Look up a given class. This enables us to make sure classes are
486
+ # singletons
487
+ def lookupclass(klassid)
488
+ unless defined? @classtable
489
+ raise Puppet::DevError, "Scope did not receive class table"
490
+ end
491
+ return @classtable[klassid]
492
+ end
518
493
 
519
- # The table for all defined objects. This will only be
520
- # used in the top scope if we don't have any nodescopes.
521
- @definedtable = Hash.new { |types, type|
522
- types[type] = {}
494
+ # Collect all of the defaults set at any higher scopes.
495
+ # This is a different type of lookup because it's additive --
496
+ # it collects all of the defaults, with defaults in closer scopes
497
+ # overriding those in later scopes.
498
+ def lookupdefaults(type)
499
+ values = {}
500
+
501
+ # first collect the values from the parents
502
+ unless @parent.nil?
503
+ @parent.lookupdefaults(type).each { |var,value|
504
+ values[var] = value
523
505
  }
506
+ end
524
507
 
525
- # A table for storing nodes.
526
- @nodetable = Hash.new(nil)
527
-
528
- # Eventually, if we support sites, this will allow definitions
529
- # of nodes with the same name in different sites. For now
530
- # the top-level scope is always the only site scope.
531
- @sitescope = true
532
-
533
- # And create a tag table, so we can collect all of the tags
534
- # associated with any objects created in this scope tree
535
- @tagtable = Hash.new { |types, type|
536
- types[type] = Hash.new { |names, name|
537
- names[name] = []
538
- }
508
+ # then override them with any current values
509
+ # this should probably be done differently
510
+ if @defaultstable.include?(type)
511
+ @defaultstable[type].each { |var,value|
512
+ values[var] = value
539
513
  }
514
+ end
515
+ #Puppet.debug "Got defaults for %s: %s" %
516
+ # [type,values.inspect]
517
+ return values
518
+ end
540
519
 
541
- @context = nil
542
- @topscope = self
543
- @type = "puppet"
544
- @name = "top"
520
+ # Look up a node by name
521
+ def lookupnode(name)
522
+ #Puppet.debug "Looking up type %s" % name
523
+ value = lookup("type",name)
524
+ if value == :undefined
525
+ return nil
526
+ else
527
+ #Puppet.debug "Found node %s" % name
528
+ return value
545
529
  end
530
+ end
546
531
 
547
- # This method abstracts recursive searching. It accepts the type
548
- # of search being done and then either a literal key to search for or
549
- # a Proc instance to do the searching.
550
- def lookup(type,sub, usecontext = false)
551
- table = @map[type]
552
- if table.nil?
553
- error = Puppet::ParseError.new(
554
- "Could not retrieve %s table at level %s" %
555
- [type,self.level]
556
- )
557
- raise error
558
- end
532
+ # Look up a defined type.
533
+ def lookuptype(name)
534
+ #Puppet.debug "Looking up type %s" % name
535
+ value = lookup("type",name)
536
+ if value == :undefined
537
+ return nil
538
+ else
539
+ #Puppet.debug "Found type %s" % name
540
+ return value
541
+ end
542
+ end
559
543
 
560
- if sub.is_a?(Proc) and obj = sub.call(table)
561
- return obj
562
- elsif table.include?(sub)
563
- return table[sub]
564
- elsif ! @parent.nil?
565
- #self.notice "Context is %s, parent %s is %s" %
566
- # [self.context, @parent.type, @parent.context]
567
- if usecontext and self.context != @parent.context
568
- return :undefined
569
- else
570
- return @parent.lookup(type,sub, usecontext)
544
+ # Look up an object by name and type. This should only look up objects
545
+ # within a class structure, not within the entire scope structure.
546
+ def lookupobject(hash)
547
+ type = hash[:type]
548
+ name = hash[:name]
549
+ #Puppet.debug "Looking up object %s of type %s in level %s" %
550
+ # [name, type, @level]
551
+ sub = proc { |table|
552
+ if table.include?(type)
553
+ if table[type].include?(name)
554
+ table[type][name]
571
555
  end
572
556
  else
573
- return :undefined
557
+ nil
574
558
  end
559
+ }
560
+ value = lookup("object",sub, true)
561
+ if value == :undefined
562
+ return nil
563
+ else
564
+ return value
575
565
  end
566
+ end
576
567
 
577
- # Look up a given class. This enables us to make sure classes are
578
- # singletons
579
- def lookupclass(klass)
580
- unless defined? @classtable
581
- raise Puppet::DevError, "Scope did not receive class table"
582
- end
583
- return @classtable[klass]
568
+ # Look up a variable. The simplest value search we do.
569
+ def lookupvar(name)
570
+ #Puppet.debug "Looking up variable %s" % name
571
+ value = lookup("variable", name)
572
+ if value == :undefined
573
+ return ""
574
+ #error = Puppet::ParseError.new(
575
+ # "Undefined variable '%s'" % name
576
+ #)
577
+ #raise error
578
+ else
579
+ return value
584
580
  end
581
+ end
585
582
 
586
- # Collect all of the defaults set at any higher scopes.
587
- # This is a different type of lookup because it's additive --
588
- # it collects all of the defaults, with defaults in closer scopes
589
- # overriding those in later scopes.
590
- def lookupdefaults(type)
591
- values = {}
592
-
593
- # first collect the values from the parents
594
- unless @parent.nil?
595
- @parent.lookupdefaults(type).each { |var,value|
596
- values[var] = value
597
- }
598
- end
599
-
600
- # then override them with any current values
601
- # this should probably be done differently
602
- if @defaultstable.include?(type)
603
- @defaultstable[type].each { |var,value|
604
- values[var] = value
605
- }
606
- end
607
- #Puppet.debug "Got defaults for %s: %s" %
608
- # [type,values.inspect]
609
- return values
583
+ # Add a new object to our object table.
584
+ def newobject(hash)
585
+ if @objectable[hash[:type]].include?(hash[:name])
586
+ raise Puppet::DevError, "Object %s[%s] is already defined" %
587
+ [hash[:type], hash[:name]]
610
588
  end
611
589
 
612
- # Look up a node by name
613
- def lookupnode(name)
614
- #Puppet.debug "Looking up type %s" % name
615
- value = self.lookup("type",name)
616
- if value == :undefined
617
- return nil
618
- else
619
- #Puppet.debug "Found node %s" % name
620
- return value
621
- end
622
- end
590
+ self.chkobjectclosure(hash)
623
591
 
624
- # Look up a defined type.
625
- def lookuptype(name)
626
- #Puppet.debug "Looking up type %s" % name
627
- value = self.lookup("type",name)
628
- if value == :undefined
629
- return nil
630
- else
631
- #Puppet.debug "Found type %s" % name
632
- return value
633
- end
634
- end
592
+ obj = nil
635
593
 
636
- # Look up an object by name and type. This should only look up objects
637
- # within a class structure, not within the entire scope structure.
638
- def lookupobject(hash)
639
- type = hash[:type]
640
- name = hash[:name]
641
- #Puppet.debug "Looking up object %s of type %s in level %s" %
642
- # [name, type, @level]
643
- sub = proc { |table|
644
- if table.include?(type)
645
- if table[type].include?(name)
646
- table[type][name]
647
- end
648
- else
649
- nil
650
- end
651
- }
652
- value = self.lookup("object",sub, true)
653
- if value == :undefined
654
- return nil
655
- else
656
- return value
657
- end
658
- end
594
+ # If it's a builtin type, then use a transobject, else use
595
+ # a ScopeObj, which will get replaced later.
596
+ if self.builtintype?(hash[:type])
597
+ obj = Puppet::TransObject.new(hash[:name], hash[:type])
659
598
 
660
- # Look up a variable. The simplest value search we do.
661
- def lookupvar(name)
662
- #Puppet.debug "Looking up variable %s" % name
663
- value = self.lookup("variable", name)
664
- if value == :undefined
665
- error = Puppet::ParseError.new(
666
- "Undefined variable '%s'" % name
667
- )
668
- raise error
669
- else
670
- return value
671
- end
599
+ @children << obj
600
+ else
601
+ obj = ScopeObj.new(nil)
602
+ obj.name = hash[:name]
603
+ obj.type = hash[:type]
672
604
  end
673
605
 
674
- # Add a new object to our object table.
675
- def newobject(hash)
676
- if @objectable[hash[:type]].include?(hash[:name])
677
- raise Puppet::DevError, "Object %s[%s] is already defined" %
678
- [hash[:type], hash[:name]]
679
- end
680
-
681
- self.chkobjectclosure(hash)
606
+ @objectable[hash[:type]][hash[:name]] = obj
682
607
 
683
- obj = nil
608
+ @definedtable[hash[:type]][hash[:name]] = obj
684
609
 
685
- # If it's a builtin type, then use a transobject, else use
686
- # a ScopeObj, which will get replaced later.
687
- if self.builtintype?(hash[:type])
688
- obj = TransObject.new(hash[:name], hash[:type])
610
+ # Keep them in order, just for kicks
611
+ @objectlist << obj
689
612
 
690
- @children << obj
691
- else
692
- obj = ScopeObj.new(nil)
693
- obj.name = hash[:name]
694
- obj.type = hash[:type]
695
- end
613
+ return obj
614
+ end
696
615
 
697
- @objectable[hash[:type]][hash[:name]] = obj
616
+ # Create a new scope.
617
+ def newscope(hash = {})
618
+ hash[:parent] = self
619
+ #debug "Creating new scope, level %s" % [self.level + 1]
620
+ return Puppet::Parser::Scope.new(hash)
621
+ end
698
622
 
699
- @definedtable[hash[:type]][hash[:name]] = obj
623
+ # Retrieve a specific node. This is used in ast.rb to find a
624
+ # parent node and in findnode to retrieve and evaluate a node.
625
+ def node(name)
626
+ @nodetable[name]
627
+ end
700
628
 
701
- # Keep them in order, just for kicks
702
- @objectlist << obj
629
+ # Store the fact that we've evaluated a given class. We use a hash
630
+ # that gets inherited from the top scope down, rather than a global
631
+ # hash. We store the object ID, not class name, so that we
632
+ # can support multiple unrelated classes with the same name.
633
+ def setclass(id, name)
634
+ unless name =~ /^[a-z]\w*$/
635
+ raise Puppet::ParseError, "Invalid class name '%s'" % name
636
+ end
703
637
 
704
- return obj
638
+ if self.topscope?
639
+ @classtable[id] = name
640
+ else
641
+ @parent.setclass(id, name)
705
642
  end
643
+ end
706
644
 
707
- # Create a new scope.
708
- def newscope(hash = {})
709
- hash[:parent] = self
710
- #Puppet.debug "Creating new scope, level %s" % [self.level + 1]
711
- return Puppet::Parser::Scope.new(hash)
645
+ # Set defaults for a type. The typename should already be downcased,
646
+ # so that the syntax is isolated.
647
+ def setdefaults(type,params)
648
+ table = @defaultstable[type]
649
+
650
+ # if we got a single param, it'll be in its own array
651
+ unless params[0].is_a?(Array)
652
+ params = [params]
712
653
  end
713
654
 
714
- # Store the fact that we've evaluated a given class. We use a hash
715
- # that gets inherited from the nodescope down, rather than a global
716
- # hash. We store the object ID, not class name, so that we
717
- # can support multiple unrelated classes with the same name.
718
- def setclass(id)
719
- if self.nodescope? or self.topscope?
720
- @classtable[id] = true
655
+ params.each { |ary|
656
+ #Puppet.debug "Default for %s is %s => %s" %
657
+ # [type,ary[0].inspect,ary[1].inspect]
658
+ if @@declarative
659
+ if table.include?(ary[0])
660
+ error = Puppet::ParseError.new(
661
+ "Default already defined for %s { %s }" %
662
+ [type,ary[0]]
663
+ )
664
+ raise error
665
+ end
721
666
  else
722
- @parent.setclass(id)
667
+ if table.include?(ary[0])
668
+ # we should maybe allow this warning to be turned off...
669
+ Puppet.warning "Replacing default for %s { %s }" %
670
+ [type,ary[0]]
671
+ end
723
672
  end
724
- end
725
-
726
- # Set defaults for a type. The typename should already be downcased,
727
- # so that the syntax is isolated.
728
- def setdefaults(type,params)
729
- table = @defaultstable[type]
673
+ table[ary[0]] = ary[1]
674
+ }
675
+ end
730
676
 
731
- # if we got a single param, it'll be in its own array
732
- unless params[0].is_a?(Array)
733
- params = [params]
734
- end
677
+ # Store a host in the site node table.
678
+ def setnode(name,code)
679
+ unless defined? @nodetable
680
+ raise Puppet::DevError, "No node table defined"
681
+ end
682
+ if @nodetable.include?(name)
683
+ raise Puppet::ParseError, "Host %s is already defined" % name
684
+ else
685
+ #Puppet.warning "Setting node %s at level %s" % [name, @level]
735
686
 
736
- params.each { |ary|
737
- #Puppet.debug "Default for %s is %s => %s" %
738
- # [type,ary[0].inspect,ary[1].inspect]
739
- if @@declarative
740
- if table.include?(ary[0])
741
- error = Puppet::ParseError.new(
742
- "Default already defined for %s { %s }" %
743
- [type,ary[0]]
744
- )
745
- raise error
746
- end
747
- else
748
- if table.include?(ary[0])
749
- # we should maybe allow this warning to be turned off...
750
- Puppet.warning "Replacing default for %s { %s }" %
751
- [type,ary[0]]
752
- end
753
- end
754
- table[ary[0]] = ary[1]
687
+ # We have to store both the scope that's setting the node and
688
+ # the node itself, so that the node gets evaluated in the correct
689
+ # scope.
690
+ @nodetable[name] = {
691
+ :scope => self,
692
+ :node => code
755
693
  }
756
694
  end
695
+ end
757
696
 
758
- # Define our type.
759
- def settype(name,ltype)
760
- # Don't let them redefine the class in this scope.
761
- if @typetable.include?(name)
762
- raise Puppet::ParseError,
763
- "%s is already defined" % name
764
- else
765
- @typetable[name] = ltype
766
- end
697
+ # Define our type.
698
+ def settype(name,ltype)
699
+ # Don't let them redefine the class in this scope.
700
+ if @typetable.include?(name)
701
+ raise Puppet::ParseError,
702
+ "%s is already defined" % name
703
+ else
704
+ @typetable[name] = ltype
767
705
  end
706
+ end
768
707
 
769
- # Return an interpolated string.
770
- def strinterp(string)
771
- newstring = string.dup
772
- regex = Regexp.new('\$\{(\w+)\}|\$(\w+)')
773
- #Puppet.debug("interpreting '%s'" % string)
774
- while match = regex.match(newstring) do
775
- if match[1]
776
- newstring.sub!(regex,self.lookupvar(match[1]).to_s)
777
- elsif match[2]
778
- newstring.sub!(regex,self.lookupvar(match[2]).to_s)
779
- else
780
- raise Puppet::DevError, "Could not match variable in %s" %
781
- newstring
708
+ # This method will fail if the named object is already defined anywhere
709
+ # in the scope tree, which is what provides some minimal closure-like
710
+ # behaviour.
711
+ def setobject(hash)
712
+ # FIXME This objectlookup stuff should be looking up using both
713
+ # the name and the namevar.
714
+
715
+ # First see if we can look the object up using normal scope
716
+ # rules, i.e., one of our parent classes has defined the
717
+ # object or something
718
+
719
+ name = hash[:name]
720
+ type = hash[:type]
721
+ params = hash[:arguments]
722
+ file = hash[:file]
723
+ line = hash[:line]
724
+
725
+ # Verify that we're not overriding any already-set parameters.
726
+ if localobj = @localobjectable[type][name]
727
+ params.each { |var, value|
728
+ if localobj.include?(var)
729
+ msg = "Cannot reassign attribute %s on %s[%s]" %
730
+ [var, type, name]
731
+
732
+ error = Puppet::ParseError.new(msg)
733
+ error.line = line
734
+ error.file = file
735
+ raise error
782
736
  end
783
- end
784
- #Puppet.debug("result is '%s'" % newstring)
785
- return newstring.gsub(/\\t/, "\t").gsub(/\\n/, "\n").gsub(/\\s/, "\s")
737
+ }
786
738
  end
787
739
 
788
- # This method will fail if the named object is already defined anywhere
789
- # in the scope tree, which is what provides some minimal closure-like
790
- # behaviour.
791
- def setobject(hash)
792
- # FIXME This objectlookup stuff should be looking up using both
793
- # the name and the namevar.
794
-
795
- # First see if we can look the object up using normal scope
796
- # rules, i.e., one of our parent classes has defined the
797
- # object or something
798
-
799
- name = hash[:name]
800
- type = hash[:type]
801
- params = hash[:arguments]
802
- file = hash[:file]
803
- line = hash[:line]
804
-
805
- # Verify that we're not overriding any already-set parameters.
806
- if localobj = @localobjectable[type][name]
807
- params.each { |var, value|
808
- if localobj.include?(var)
809
- msg = "Cannot reassign attribute %s on %s[%s]" %
810
- [var, type, name]
811
-
812
- error = Puppet::ParseError.new(msg)
813
- error.line = line
814
- error.file = file
815
- raise error
816
- end
817
- }
818
- end
819
-
820
- if objecttype = self.lookuptype(type)
821
- # It's a defined type
822
- objecttype.safeevaluate(
823
- :name => name,
824
- :type => type,
825
- :arguments => params,
826
- :scope => self
827
- )
828
- else
829
- # First look for it in a parent scope
830
- obj = self.lookupobject(:name => name, :type => type)
831
-
832
- unless obj and obj != :undefined
833
- unless obj = @objectable[type][name]
834
- obj = self.newobject(
835
- :type => type,
836
- :name => name,
837
- :line => line,
838
- :file => file
839
- )
840
-
841
- # only set these if we've created the object,
842
- # which is the most common case
843
- # FIXME we eventually need to store the file
844
- # and line with each param, not the object
845
- # itself.
846
- obj.file = file
847
- obj.line = line
848
- end
740
+ if objecttype = lookuptype(type)
741
+ # It's a defined type
742
+ objecttype.safeevaluate(
743
+ :name => name,
744
+ :type => type,
745
+ :arguments => params,
746
+ :scope => self
747
+ )
748
+ else
749
+ # First look for it in a parent scope
750
+ obj = lookupobject(:name => name, :type => type)
751
+
752
+ unless obj and obj != :undefined
753
+ unless obj = @objectable[type][name]
754
+ obj = self.newobject(
755
+ :type => type,
756
+ :name => name,
757
+ :line => line,
758
+ :file => file
759
+ )
849
760
 
850
- # Now add our parameters. This has the function of
851
- # overriding existing values, which might have been
852
- # defined in a higher scope.
761
+ # only set these if we've created the object,
762
+ # which is the most common case
763
+ # FIXME we eventually need to store the file
764
+ # and line with each param, not the object
765
+ # itself.
766
+ obj.file = file
767
+ obj.line = line
853
768
  end
854
- params.each { |var,value|
855
- # Add it to our found object
856
- obj[var] = value
857
- }
858
769
  end
859
770
 
860
- @localobjectable[type][name] ||= {}
861
-
771
+ # Now add our parameters. This has the function of overriding
772
+ # existing values, which might have been defined in a higher
773
+ # scope.
862
774
  params.each { |var,value|
863
- # And add it to the local table; mmm, hack
864
- @localobjectable[type][name][var] = value
775
+ # Add it to our found object
776
+ obj[var] = value
865
777
  }
778
+ end
866
779
 
867
- return obj
780
+ @localobjectable[type][name] ||= {}
781
+
782
+ params.each { |var,value|
783
+ # And add it to the local table; mmm, hack
784
+ @localobjectable[type][name][var] = value
785
+ }
786
+
787
+ return obj
788
+ end
789
+
790
+ # Set a variable in the current scope. This will override settings
791
+ # in scopes above, but will not allow variables in the current scope
792
+ # to be reassigned if we're declarative (which is the default).
793
+ def setvar(name,value)
794
+ #Puppet.debug "Setting %s to '%s' at level %s" %
795
+ # [name.inspect,value,self.level]
796
+ if @@declarative and @symtable.include?(name)
797
+ raise Puppet::ParseError, "Cannot reassign variable %s" % name
798
+ else
799
+ if @symtable.include?(name)
800
+ Puppet.warning "Reassigning %s to %s" % [name,value]
801
+ end
802
+ @symtable[name] = value
868
803
  end
804
+ end
869
805
 
870
- # Set a variable in the current scope. This will override settings
871
- # in scopes above, but will not allow variables in the current scope
872
- # to be reassigned if we're declarative (which is the default).
873
- def setvar(name,value)
874
- #Puppet.debug "Setting %s to '%s' at level %s" %
875
- # [name.inspect,value,self.level]
876
- if @@declarative and @symtable.include?(name)
877
- raise Puppet::ParseError, "Cannot reassign variable %s" % name
806
+ # Return an interpolated string.
807
+ def strinterp(string)
808
+ newstring = string.dup
809
+ regex = Regexp.new('\$\{(\w+)\}|\$(\w+)')
810
+ #Puppet.debug("interpreting '%s'" % string)
811
+ while match = regex.match(newstring) do
812
+ if match[1]
813
+ newstring.sub!(regex,lookupvar(match[1]).to_s)
814
+ elsif match[2]
815
+ newstring.sub!(regex,lookupvar(match[2]).to_s)
878
816
  else
879
- if @symtable.include?(name)
880
- Puppet.warning "Reassigning %s to %s" % [name,value]
881
- end
882
- @symtable[name] = value
817
+ raise Puppet::DevError, "Could not match variable in %s" %
818
+ newstring
883
819
  end
884
820
  end
821
+ #Puppet.debug("result is '%s'" % newstring)
822
+ return newstring.gsub(/\\t/, "\t").gsub(/\\n/, "\n").gsub(/\\s/, "\s")
823
+ end
824
+
825
+ # Add a tag to our current list. These tags will be added to all
826
+ # of the objects contained in this scope.
827
+ def tag(*ary)
828
+ ary.each { |tag|
829
+ if tag.nil? or tag == ""
830
+ Puppet.debug "got told to tag with %s" % tag.inspect
831
+ next
832
+ end
833
+ unless @tags.include?(tag)
834
+ #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
835
+ @tags << tag.to_s
836
+ end
837
+ }
838
+ end
885
839
 
886
- # Add a tag to our current list. These tags will be added to all
887
- # of the objects contained in this scope.
888
- def tag(*ary)
889
- ary.each { |tag|
840
+ # Return the tags associated with this scope. It's basically
841
+ # just our parents' tags, plus our type.
842
+ def tags
843
+ tmp = [] + @tags
844
+ unless ! defined? @type or @type.nil? or @type == ""
845
+ tmp << @type.to_s
846
+ end
847
+ if @parent
848
+ @parent.tags.each { |tag|
890
849
  if tag.nil? or tag == ""
891
- Puppet.debug "got told to tag with %s" % tag.inspect
850
+ Puppet.debug "parent returned tag %s" % tag.inspect
892
851
  next
893
852
  end
894
- unless @tags.include?(tag)
895
- #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
896
- @tags << tag.to_s
853
+ unless tmp.include?(tag)
854
+ tmp << tag
897
855
  end
898
856
  }
899
857
  end
858
+ return tmp
859
+ end
900
860
 
901
- # Return the tags associated with this scope. It's basically
902
- # just our parents' tags, plus our type.
903
- def tags
904
- tmp = [] + @tags
905
- unless ! defined? @type or @type.nil? or @type == ""
906
- tmp << @type.to_s
907
- end
908
- if @parent
909
- @parent.tags.each { |tag|
910
- if tag.nil? or tag == ""
911
- Puppet.debug "parent returned tag %s" % tag.inspect
912
- next
913
- end
914
- unless tmp.include?(tag)
915
- tmp << tag
916
- end
917
- }
918
- end
919
- return tmp
861
+ # Used mainly for logging
862
+ def to_s
863
+ if @name
864
+ return "%s[%s]" % [@type, @name]
865
+ else
866
+ return @type.to_s
920
867
  end
868
+ end
921
869
 
922
- # Used mainly for logging
923
- def to_s
924
- if @name
925
- return "%s[%s]" % [@type, @name]
870
+ # Convert our scope to a list of Transportable objects.
871
+ def to_trans
872
+
873
+ results = []
874
+
875
+ # Iterate across our child scopes and call to_trans on them
876
+ @children.each { |child|
877
+ if child.is_a?(Scope)
878
+ cresult = child.to_trans
879
+
880
+ # Scopes normally result in a TransBucket, but they could
881
+ # also result in a normal array; if that happens, get rid
882
+ # of the array.
883
+ unless cresult.is_a?(Puppet::TransBucket)
884
+ cresult.each { |result|
885
+ results.push(result)
886
+ }
887
+ else
888
+ unless cresult.empty?
889
+ # Otherwise, just add it to our list of results.
890
+ results.push(cresult)
891
+ end
892
+ end
893
+ elsif child.is_a?(Puppet::TransObject)
894
+ if child.empty?
895
+ next
896
+ end
897
+ # Wait until the last minute to set tags, although this
898
+ # probably should not matter
899
+ child.tags = self.tags
900
+
901
+ # Add any defaults.
902
+ self.adddefaults(child)
903
+
904
+ # Then make sure this child's tags are stored in the
905
+ # central table. This should maybe be in the evaluate
906
+ # methods, but, eh.
907
+ @topscope.addtags(child)
908
+ results.push(child)
926
909
  else
927
- return @type.to_s
910
+ raise Puppet::DevError,
911
+ "Puppet::Parse::Scope cannot handle objects of type %s" %
912
+ child.class
928
913
  end
929
- end
914
+ }
930
915
 
931
- # Convert our scope to a list of Transportable objects.
932
- def to_trans
933
- #unless self.finished?
934
- # raise Puppet::DevError, "%s not finished" % self.type
935
- # self.err "Not finished"
936
- # self.finish
937
- #end
938
- #Puppet.debug "Translating scope %s at level %s" %
939
- # [self.object_id,self.level]
940
-
941
- results = []
942
-
943
- # Iterate across our child scopes and call to_trans on them
944
- @children.each { |child|
945
- if child.is_a?(Scope)
946
- cresult = child.to_trans
947
- #Puppet.debug "Got %s from scope %s" %
948
- # [cresult.class,child.object_id]
949
-
950
- # Scopes normally result in a TransBucket, but they could
951
- # also result in a normal array; if that happens, get rid
952
- # of the array.
953
- unless cresult.is_a?(TransBucket)
954
- cresult.each { |result|
955
- results.push(result)
956
- }
957
- else
958
- unless cresult.empty?
959
- # Otherwise, just add it to our list of results.
960
- results.push(cresult)
961
- end
962
- end
916
+ # Get rid of any nil objects.
917
+ results = results.reject { |child|
918
+ child.nil?
919
+ }
963
920
 
964
- # Nodescopes are one-time; once they've been evaluated
965
- # I need to destroy them. Nodeclean makes sure this is
966
- # done correctly, but this should catch most of them.
967
- if child.nodescope?
968
- @children.delete(child)
969
- end
970
- elsif child.is_a?(TransObject)
971
- if child.empty?
972
- next
973
- end
974
- # Wait until the last minute to set tags, although this
975
- # probably should not matter
976
- child.tags = self.tags
977
-
978
- # Then make sure this child's tags are stored in the
979
- # central table. This should maybe be in the evaluate
980
- # methods, but, eh.
981
- @topscope.addtags(child)
982
- results.push(child)
983
- else
984
- raise Puppet::DevError,
985
- "Puppet::Parse::Scope cannot handle objects of type %s" %
986
- child.class
987
- end
988
- }
921
+ # If we have a name and type, then make a TransBucket, which
922
+ # becomes a component.
923
+ # Else, just stack all of the objects into the current bucket.
924
+ if @type
925
+ bucket = Puppet::TransBucket.new
989
926
 
990
- # Get rid of any nil objects.
991
- results = results.reject { |child|
992
- child.nil?
993
- }
927
+ if defined? @name and @name
928
+ bucket.name = @name
929
+ end
994
930
 
995
- # If we have a name and type, then make a TransBucket, which
996
- # becomes a component.
997
- # Else, just stack all of the objects into the current bucket.
998
- if @type
999
- bucket = TransBucket.new
931
+ # it'd be nice not to have to do this...
932
+ results.each { |result|
933
+ #Puppet.warning "Result type is %s" % result.class
934
+ bucket.push(result)
935
+ }
936
+ if defined? @type
937
+ bucket.type = @type
938
+ else
939
+ raise Puppet::ParseError,
940
+ "No type for scope %s" % @name
941
+ end
1000
942
 
1001
- if defined? @name and @name
1002
- bucket.name = @name
1003
- end
943
+ if defined? @keyword
944
+ bucket.keyword = @keyword
945
+ end
946
+ #Puppet.debug(
947
+ # "TransBucket with name %s and type %s in scope %s" %
948
+ # [@name,@type,self.object_id]
949
+ #)
1004
950
 
1005
- # it'd be nice not to have to do this...
1006
- results.each { |result|
1007
- #Puppet.warning "Result type is %s" % result.class
1008
- bucket.push(result)
1009
- }
1010
- if defined? @type
1011
- bucket.type = @type
951
+ # now find metaparams
952
+ @symtable.each { |var,value|
953
+ if Puppet::Type.metaparam?(var.intern)
954
+ #Puppet.debug("Adding metaparam %s" % var)
955
+ bucket.param(var,value)
1012
956
  else
1013
- raise Puppet::ParseError,
1014
- "No type for scope %s" % @name
957
+ #Puppet.debug("%s is not a metaparam" % var)
1015
958
  end
959
+ }
960
+ #Puppet.debug "Returning bucket %s from scope %s" %
961
+ # [bucket.name,self.object_id]
962
+ return bucket
963
+ else
964
+ Puppet.debug "nameless scope; just returning a list"
965
+ return results
966
+ end
967
+ end
1016
968
 
1017
- if defined? @keyword
1018
- bucket.keyword = @keyword
1019
- end
1020
- #Puppet.debug(
1021
- # "TransBucket with name %s and type %s in scope %s" %
1022
- # [@name,@type,self.object_id]
1023
- #)
1024
-
1025
- # now find metaparams
1026
- @symtable.each { |var,value|
1027
- if Puppet::Type.metaparam?(var.intern)
1028
- #Puppet.debug("Adding metaparam %s" % var)
1029
- bucket.param(var,value)
1030
- else
1031
- #Puppet.debug("%s is not a metaparam" % var)
1032
- end
1033
- }
1034
- #Puppet.debug "Returning bucket %s from scope %s" %
1035
- # [bucket.name,self.object_id]
1036
- return bucket
969
+ protected
970
+
971
+ # This method abstracts recursive searching. It accepts the type
972
+ # of search being done and then either a literal key to search for or
973
+ # a Proc instance to do the searching.
974
+ def lookup(type,sub, usecontext = false)
975
+ table = @map[type]
976
+ if table.nil?
977
+ error = Puppet::ParseError.new(
978
+ "Could not retrieve %s table at level %s" %
979
+ [type,self.level]
980
+ )
981
+ raise error
982
+ end
983
+
984
+ if sub.is_a?(Proc) and obj = sub.call(table)
985
+ return obj
986
+ elsif table.include?(sub)
987
+ return table[sub]
988
+ elsif ! @parent.nil?
989
+ #self.notice "Context is %s, parent %s is %s" %
990
+ # [self.context, @parent.type, @parent.context]
991
+ if usecontext and self.context != @parent.context
992
+ return :undefined
1037
993
  else
1038
- #Puppet.debug "nameless scope; just returning a list"
1039
- return results
994
+ return @parent.lookup(type,sub, usecontext)
1040
995
  end
996
+ else
997
+ return :undefined
1041
998
  end
1042
999
  end
1043
1000
  end
1044
1001
  end
1045
1002
 
1046
- # $Id: scope.rb 957 2006-02-28 03:32:51Z luke $
1003
+ # $Id: scope.rb 1104 2006-04-11 17:48:14Z luke $