~ubuntu-branches/ubuntu/lucid/puppet/lucid-security

« back to all changes in this revision

Viewing changes to lib/puppet/parser/parser_support.rb

  • Committer: Bazaar Package Importer
  • Author(s): Chuck Short
  • Date: 2009-12-23 00:48:10 UTC
  • mfrom: (1.1.10 upstream) (3.1.7 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091223004810-3i4oryds922g5n59
Tags: 0.25.1-3ubuntu1
* Merge from debian testing.  Remaining changes:
  - debian/rules:
    + Don't start puppet when first installing puppet.
  - debian/puppet.conf, lib/puppet/defaults.rb:
    + Move templates to /etc/puppet
  - lib/puppet/defaults.rb:
    + Fix /var/lib/puppet/state ownership.
  - man/man8/puppet.conf.8: 
    + Fix broken URL in manpage.
  - debian/control:
    + Update maintainer accordint to spec.
    + Puppetmaster Recommends -> Suggests
    + Created puppet-testsuite as a seperate. Allow the users to run puppet's 
      testsuite.
  - tests/Rakefile: Fix rakefile so that the testsuite can acutally be ran.

Show diffs side-by-side

added added

removed removed

Lines of Context:
2
2
# tired of rebuilding the parser.rb file all the time.
3
3
class Puppet::Parser::Parser
4
4
    require 'puppet/parser/functions'
5
 
 
6
 
    ASTSet = Struct.new(:classes, :definitions, :nodes)
7
 
 
8
 
    # Define an accessor method for each table.  We hide the existence of
9
 
    # the struct.
10
 
    [:classes, :definitions, :nodes].each do |name|
11
 
        define_method(name) do
12
 
            @astset.send(name)
13
 
        end
14
 
    end
 
5
    require 'puppet/parser/files'
 
6
    require 'puppet/parser/loaded_code'
 
7
    require 'monitor'
15
8
 
16
9
    AST = Puppet::Parser::AST
17
10
 
58
51
        end
59
52
 
60
53
        k = klass.new(hash)
61
 
        k.doc = lexer.getcomment if !k.nil? and k.use_docs and k.doc.empty?
 
54
        k.doc = lexer.getcomment(hash[:line]) if !k.nil? and k.use_docs and k.doc.empty?
62
55
        return k
63
56
    end
64
57
 
105
98
        end
106
99
    end
107
100
 
108
 
    # Find a class definition, relative to the current namespace.
109
 
    def findclass(namespace, name)
110
 
        fqfind namespace, name, classes
111
 
    end
112
 
 
113
 
    # Find a component definition, relative to the current namespace.
114
 
    def finddefine(namespace, name)
115
 
        fqfind namespace, name, definitions
116
 
    end
117
 
 
118
 
    # This is only used when nodes are looking up the code for their
119
 
    # parent nodes.
120
 
    def findnode(name)
121
 
        fqfind "", name, nodes
122
 
    end
123
 
 
124
 
    # The recursive method used to actually look these objects up.
125
 
    def fqfind(namespace, name, table)
 
101
    [:hostclass, :definition, :node, :nodes?].each do |method|
 
102
        define_method(method) do |*args|
 
103
            @loaded_code.send(method, *args)
 
104
        end
 
105
    end
 
106
 
 
107
    def find_hostclass(namespace, name)
 
108
        find_or_load(namespace, name, :hostclass)
 
109
    end
 
110
 
 
111
    def find_definition(namespace, name)
 
112
        find_or_load(namespace, name, :definition)
 
113
    end
 
114
 
 
115
    def find_or_load(namespace, name, type)
 
116
        method = "find_#{type}"
126
117
        namespace = namespace.downcase
127
 
        name = name.to_s.downcase
128
 
 
129
 
        # If our classname is fully qualified or we have no namespace,
130
 
        # just try directly for the class, and return either way.
131
 
        if name =~ /^::/ or namespace == ""
132
 
            classname = name.sub(/^::/, '')
133
 
            self.load(classname) unless table[classname]
134
 
            return table[classname]
135
 
        end
136
 
 
137
 
        # Else, build our namespace up piece by piece, checking
138
 
        # for the class in each namespace.
139
 
        ary = namespace.split("::")
140
 
 
141
 
        while ary.length > 0
142
 
            newname = (ary + [name]).join("::").sub(/^::/, '')
143
 
            if obj = table[newname] or (self.load(newname) and obj = table[newname])
144
 
                return obj
145
 
            end
146
 
 
147
 
            # Delete the second to last object, which reduces our namespace by one.
148
 
            ary.pop
149
 
        end
150
 
 
151
 
        # If we've gotten to this point without finding it, see if the name
152
 
        # exists at the top namespace
153
 
        if obj = table[name] or (self.load(name) and obj = table[name])
154
 
            return obj
155
 
        end
156
 
 
157
 
        return nil
 
118
        name      = name.downcase
 
119
        fullname = (namespace + "::" + name).sub(/^::/, '')
 
120
 
 
121
        if name =~ /^::/
 
122
            names_to_try = [name.sub(/^::/, '')]
 
123
        else
 
124
            names_to_try = [fullname]
 
125
 
 
126
            # Try to load the module init file if we're a qualified name
 
127
            names_to_try << fullname.split("::")[0] if fullname.include?("::")
 
128
 
 
129
            # Otherwise try to load the bare name on its own.  This
 
130
            # is appropriate if the class we're looking for is in a
 
131
            # module that's different from our namespace.
 
132
            names_to_try << name
 
133
            names_to_try.compact!
 
134
        end
 
135
 
 
136
        until (result = @loaded_code.send(method, namespace, name)) or names_to_try.empty? do
 
137
            self.load(names_to_try.shift)
 
138
        end
 
139
        return result
158
140
    end
159
141
 
160
142
    # Import our files.
175
157
 
176
158
        # We can't interpolate at this point since we don't have any
177
159
        # scopes set up. Warn the user if they use a variable reference
 
160
        raise "Got no file" unless file
178
161
        pat = file
179
162
        if pat.index("$")
180
163
            Puppet.warning(
183
166
               "in file #{@lexer.file} at line #{@lexer.line}"
184
167
            )
185
168
        end
186
 
        files = Puppet::Module::find_manifests(pat, :cwd => dir, :environment => @environment)
 
169
        files = Puppet::Parser::Files.find_manifests(pat, :cwd => dir, :environment => @environment)
187
170
        if files.size == 0
188
171
            raise Puppet::ImportError.new("No file(s) found for import " +
189
172
                                          "of '#{pat}'")
190
173
        end
191
174
 
192
175
        files.collect { |file|
193
 
            parser = Puppet::Parser::Parser.new(:astset => @astset, :environment => @environment)
 
176
            parser = Puppet::Parser::Parser.new(:loaded_code => @loaded_code, :environment => @environment)
194
177
            parser.files = self.files
195
178
            Puppet.debug("importing '%s'" % file)
196
179
 
210
193
    end
211
194
 
212
195
    def initialize(options = {})
213
 
        @astset = options[:astset] || ASTSet.new({}, {}, {})
 
196
        @loaded_code = options[:loaded_code] || Puppet::Parser::LoadedCode.new
214
197
        @environment = options[:environment]
215
198
        initvars()
216
199
    end
220
203
        @lexer = Puppet::Parser::Lexer.new()
221
204
        @files = {}
222
205
        @loaded = []
 
206
        @loading = {}
 
207
        @loading.extend(MonitorMixin)
 
208
        class << @loading
 
209
            def done_with(item)
 
210
                synchronize do 
 
211
                    delete(item)[:busy].signal if self.has_key?(item) and self[item][:loader] == Thread.current
 
212
                end
 
213
            end
 
214
            def owner_of(item)
 
215
                synchronize do
 
216
                    if !self.has_key? item
 
217
                        self[item] = { :loader => Thread.current, :busy => self.new_cond}
 
218
                        :nobody
 
219
                      elsif self[item][:loader] == Thread.current
 
220
                        :this_thread
 
221
                      else
 
222
                        flag = self[item][:busy]
 
223
                        flag.wait
 
224
                        flag.signal
 
225
                        :another_thread
 
226
                    end
 
227
                end
 
228
            end
 
229
        end
 
230
    end
 
231
 
 
232
    # Utility method factored out of load
 
233
    def able_to_import?(classname,item,msg)
 
234
        unless @loaded.include?(item)
 
235
            begin
 
236
              case @loading.owner_of(item)
 
237
              when :this_thread
 
238
                  return
 
239
              when :another_thread
 
240
                  return able_to_import?(classname,item,msg)
 
241
              when :nobody
 
242
                  import(item)
 
243
                  Puppet.info "Autoloaded #{msg}"
 
244
                  @loaded << item
 
245
              end
 
246
            rescue Puppet::ImportError => detail
 
247
                # We couldn't load the item
 
248
            ensure
 
249
                @loading.done_with(item)
 
250
            end
 
251
        end
 
252
        # We don't know whether we're looking for a class or definition, so we have
 
253
        # to test for both.
 
254
        return @loaded_code.hostclass(classname) || @loaded_code.definition(classname)
223
255
    end
224
256
 
225
257
    # Try to load a class, since we could not find it.
226
258
    def load(classname)
227
259
        return false if classname == ""
228
260
        filename = classname.gsub("::", File::SEPARATOR)
229
 
 
230
 
        # First try to load the top-level module
231
261
        mod = filename.scan(/^[\w-]+/).shift
232
 
        unless @loaded.include?(mod)
233
 
            @loaded << mod
234
 
            begin
235
 
                import(mod)
236
 
                Puppet.info "Autoloaded module %s" % mod
237
 
            rescue Puppet::ImportError => detail
238
 
                # We couldn't load the module
239
 
            end
240
 
        end
241
 
 
242
 
        # We don't know whether we're looking for a class or definition, so we have
243
 
        # to test for both.
244
 
        return true if classes.include?(classname) || definitions.include?(classname)
245
 
 
246
 
        unless @loaded.include?(filename)
247
 
            @loaded << filename
248
 
            # Then the individual file
249
 
            begin
250
 
                import(filename)
251
 
                Puppet.info "Autoloaded file %s from module %s" % [filename, mod]
252
 
            rescue Puppet::ImportError => detail
253
 
                # We couldn't load the file
254
 
            end
255
 
        end
256
 
        # We don't know whether we're looking for a class or definition, so we have
257
 
        # to test for both.
258
 
        return classes.include?(classname) || definitions.include?(classname)
 
262
 
 
263
        # First try to load the top-level module then the individual file
 
264
        [[mod,     "module %s"              %            mod ],
 
265
         [filename,"file %s from module %s" % [filename, mod]]
 
266
        ].any? { |item,description| able_to_import?(classname,item,description) }
259
267
    end
260
268
 
261
269
    # Split an fq name into a namespace and name
270
278
    def newclass(name, options = {})
271
279
        name = name.downcase
272
280
 
273
 
        if definitions.include?(name)
 
281
        if @loaded_code.definition(name)
274
282
            raise Puppet::ParseError, "Cannot redefine class %s as a definition" % name
275
283
        end
276
284
        code = options[:code]
278
286
        doc = options[:doc]
279
287
 
280
288
        # If the class is already defined, then add code to it.
281
 
        if other = @astset.classes[name]
 
289
        if other = @loaded_code.hostclass(name) || @loaded_code.definition(name)
282
290
            # Make sure the parents match
283
291
            if parent and other.parentclass and (parent != other.parentclass)
284
292
                error("Class %s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
323
331
            args[:code] = code if code
324
332
            args[:parentclass] = parent if parent
325
333
            args[:doc] = doc
 
334
            args[:line] = options[:line]
326
335
 
327
 
            @astset.classes[name] = ast AST::HostClass, args
 
336
            @loaded_code.add_hostclass(name, ast(AST::HostClass, args))
328
337
        end
329
338
 
330
 
        return @astset.classes[name]
 
339
        return @loaded_code.hostclass(name)
331
340
    end
332
341
 
333
342
    # Create a new definition.
334
343
    def newdefine(name, options = {})
335
344
        name = name.downcase
336
 
        if @astset.classes.include?(name)
 
345
        if @loaded_code.hostclass(name)
337
346
            raise Puppet::ParseError, "Cannot redefine class %s as a definition" %
338
347
                name
339
348
        end
340
349
        # Make sure our definition doesn't already exist
341
 
        if other = @astset.definitions[name]
 
350
        if other = @loaded_code.definition(name)
342
351
            error("%s is already defined at %s:%s; cannot redefine" % [name, other.file, other.line])
343
352
        end
344
353
 
349
358
            :code => options[:code],
350
359
            :parser => self,
351
360
            :classname => name,
352
 
            :doc => options[:doc]
 
361
            :doc => options[:doc],
 
362
            :line => options[:line]
353
363
        }
354
364
 
355
365
        [:code, :arguments].each do |param|
356
366
            args[param] = options[param] if options[param]
357
367
        end
358
368
 
359
 
        @astset.definitions[name] = ast AST::Definition, args
 
369
        @loaded_code.add_definition(name, ast(AST::Definition, args))
360
370
    end
361
371
 
362
372
    # Create a new node.  Nodes are special, because they're stored in a global
365
375
        names = [names] unless names.instance_of?(Array)
366
376
        doc = lexer.getcomment
367
377
        names.collect do |name|
368
 
            name = name.to_s.downcase
369
 
            if other = @astset.nodes[name]
 
378
            name = AST::HostName.new :value => name unless name.is_a?(AST::HostName)
 
379
            if other = @loaded_code.node_exists?(name)
370
380
                error("Node %s is already defined at %s:%s; cannot redefine" % [other.name, other.file, other.line])
371
381
            end
372
382
            name = name.to_s if name.is_a?(Symbol)
373
383
            args = {
374
384
                :name => name,
375
385
                :parser => self,
376
 
                :doc => doc
 
386
                :doc => doc,
 
387
                :line => options[:line]
377
388
            }
378
389
            if options[:code]
379
390
                args[:code] = options[:code]
381
392
            if options[:parent]
382
393
                args[:parentclass] = options[:parent]
383
394
            end
384
 
            @astset.nodes[name] = ast(AST::Node, args)
385
 
            @astset.nodes[name].classname = name
386
 
            @astset.nodes[name]
 
395
            node = ast(AST::Node, args)
 
396
            node.classname = name.to_classname
 
397
            @loaded_code.add_node(name, node)
 
398
            node
387
399
        end
388
400
    end
389
401
 
391
403
        if token == 0 # denotes end of file
392
404
            value = 'end of file'
393
405
        else
394
 
            value = "'%s'" % value
 
406
            value = "'%s'" % value[:value]
395
407
        end
396
408
        error = "Syntax error at %s" % [value]
397
409
 
446
458
            # Store the results as the top-level class.
447
459
            newclass("", :code => main)
448
460
        end
449
 
        @version = Time.now.to_i
450
 
        return @astset
 
461
        return @loaded_code
451
462
    ensure
452
463
        @lexer.clear
453
464
    end
465
476
        @lexer.string = string
466
477
    end
467
478
 
 
479
    def version
 
480
        return @version if defined?(@version)
 
481
 
 
482
        if Puppet[:config_version] == ""
 
483
            @version = Time.now.to_i
 
484
            return @version
 
485
        end
 
486
 
 
487
        @version = Puppet::Util.execute([Puppet[:config_version]]).strip
 
488
 
 
489
    rescue Puppet::ExecutionFailure => e
 
490
        raise Puppet::ParseError, "Unable to set config_version: #{e.message}"
 
491
    end
 
492
 
468
493
    # Add a new file to be checked when we're checking to see if we should be
469
494
    # reparsed.  This is basically only used by the TemplateWrapper to let the
470
495
    # parser know about templates that should be parsed.
475
500
    private
476
501
 
477
502
    def check_and_add_to_watched_files(filename)
478
 
        unless @files.include?(filename)    
 
503
        unless @files.include?(filename)
479
504
            @files[filename] = Puppet::Util::LoadedFile.new(filename)
480
505
            return true
481
506
        else