diff --git a/lib/puppet/loadedfile.rb b/lib/puppet/loadedfile.rb index c0b00c597..b0e408475 100755 --- a/lib/puppet/loadedfile.rb +++ b/lib/puppet/loadedfile.rb @@ -1,62 +1,66 @@ # A simple class that tells us when a file has changed and thus whether we # should reload it require 'puppet' module Puppet class NoSuchFile < Puppet::Error; end class LoadedFile - attr_reader :file + attr_reader :file, :statted # Provide a hook for setting the timestamp during testing, so we don't # have to depend on the granularity of the filesystem. attr_writer :tstamp Puppet.config.setdefaults(:puppet, :filetimeout => [ 15, "The minimum time to wait between checking for updates in configuration files." ] ) # Determine whether the file has changed and thus whether it should - # be reparsed + # be reparsed. def changed? - # Don't actually stat the file more often than filetimeout. - if Time.now - @statted >= Puppet[:filetimeout] - tmp = stamp() - - if tmp == @tstamp - return false - else - @tstamp = tmp - return true - end - else + tmp = stamp() + + # We use a different internal variable than the stamp method + # because it doesn't keep historical state and we do -- that is, + # we will always be comparing two timestamps, whereas + # stamp() just always wants the latest one. + if tmp == @tstamp return false + else + @tstamp = tmp + return @tstamp end end # Create the file. Must be passed the file path. def initialize(file) @file = file unless FileTest.exists?(@file) - raise Puppet::NoSuchFile, "Can not use a non-existent file for parsing" + raise Puppet::NoSuchFile, + "Can not use a non-existent file for parsing" end + @statted = 0 @tstamp = stamp() end - def to_s - @file + # Retrieve the filestamp, but only refresh it if we're beyond our + # filetimeout + def stamp + if @stamp.nil? or (Time.now.to_i - @statted >= Puppet[:filetimeout]) + @statted = Time.now.to_i + @stamp = File.stat(@file).ctime + end + return @stamp end - private - - def stamp - @statted = Time.now - return File.stat(@file).ctime + def to_s + @file end end end # $Id$ diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index 623ca0cab..3a1a78e50 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -1,747 +1,744 @@ # vim: syntax=ruby # the parser class Puppet::Parser::Parser token LBRACK DQTEXT SQTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE token FALSE EQUALS LESSEQUAL NOTEQUAL DOT COLON TYPE token QMARK LPAREN RPAREN ISEQUAL GREATEREQUAL GREATERTHAN LESSTHAN token IF ELSE IMPORT DEFINE ELSIF VARIABLE CLASS INHERITS NODE BOOLEAN token NAME SEMIC CASE DEFAULT AT LCOLLECT RCOLLECT # We have 2 shift/reduce conflicts #expect 2 rule program: statements { # Make sure we always return an array. if val[0].is_a?(AST::ASTArray) result = val[0] else result = aryfy(val[0]) end } | nothing statements: statement | statements statement { if val[0].instance_of?(AST::ASTArray) val[0].push(val[1]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[1]] end } # The main list of valid statements statement: object | collectable | collection | assignment | casestatement | ifstatement | import | fstatement | definition | hostclass | nodedef fstatement: NAME LPAREN classnames RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement } | NAME classnames { args = aryfy(val[1]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement } # Includes are just syntactic sugar for classes with no names and # no arguments. #include: INCLUDE classnames { # result = function_include(val[1]) #} # Define a new tag. Both of these functions should really be done generically, # but I'm not in a position to do that just yet. :/ #tag: TAG classnames { # result = function_tag(val[1]) #} classnames: classname | classnames COMMA classname { result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file } classname: name | variable | quotedtext #object: name LBRACE objectname COLON params endcomma RBRACE { object: name LBRACE objectinstances endsemi RBRACE { if val[0].instance_of?(AST::ASTArray) raise Puppet::ParseError, "Invalid name" end array = val[2] if array.instance_of?(AST::ObjectInst) array = [array] end result = ast AST::ASTArray # this iterates across each specified objectinstance array.each { |instance| unless instance.instance_of?(AST::ObjectInst) raise Puppet::Dev, "Got something that isn't an instance" end # now, i need to somehow differentiate between those things with # arrays in their names, and normal things result.push ast(AST::ObjectDef, :type => val[0], :name => instance[0], :params => instance[1]) } } | name LBRACE params endcomma RBRACE { if val[0].instance_of?(AST::ASTArray) Puppet.notice "invalid name" raise Puppet::ParseError, "Invalid name" end # an object but without a name # this cannot be an instance of a library type result = ast AST::ObjectDef, :type => val[0], :params => val[2] } | type LBRACE params endcomma RBRACE { # a template setting for a type if val[0].instance_of?(AST::ASTArray) raise Puppet::ParseError, "Invalid type" end result = ast(AST::TypeDefaults, :type => val[0], :params => val[2]) } # Collectable objects; these get stored in the database, instead of # being passed to the client. collectable: AT object { unless Puppet[:storeconfigs] raise Puppet::ParseError, "You cannot collect without storeconfigs being set" end if val[1].is_a? AST::TypeDefaults raise Puppet::ParseError, "Defaults are not collectable" end # Just mark our objects as collectable and pass them through. if val[1].instance_of?(AST::ASTArray) val[1].each do |obj| obj.collectable = true end else val[1].collectable = true end result = val[1] } # A collection statement. Currently supports no arguments at all, but eventually # will, I assume. collection: name LCOLLECT RCOLLECT { unless Puppet[:storeconfigs] raise Puppet::ParseError, "You cannot collect without storeconfigs being set" end result = ast AST::Collection, :type => val[0] } objectinst: objectname COLON params endcomma { result = ast AST::ObjectInst, :children => [val[0],val[2]] } objectinstances: objectinst | objectinstances SEMIC objectinst { if val[0].instance_of?(AST::ObjectInst) result = ast AST::ASTArray, :children => [val[0],val[2]] else val[0].push val[2] result = val[0] end } endsemi: # nothing | SEMIC name: NAME { result = ast AST::Name, :value => val[0] } type: TYPE { result = ast AST::Type, :value => val[0] } objectname: quotedtext | name | type | selector | variable | array assignment: VARIABLE EQUALS rvalue { # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0].sub(/^\$/,'') result = ast AST::VarDef, :name => variable, :value => val[2] } params: # nothing { result = ast AST::ASTArray } | param { result = val[0] } | params COMMA param { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } param: NAME FARROW rvalue { leaf = ast AST::String, :value => val[0] result = ast AST::ObjectParam, :param => leaf, :value => val[2] } rvalues: rvalue | rvalues comma rvalue { if val[0].instance_of?(AST::ASTArray) result = val[0].push(val[2]) else result = ast AST::ASTArray, :children => [val[0],val[2]] end } rvalue: quotedtext | name | type | boolean | selector | variable | array | objectref | funcrvalue # We currently require arguments in these functions. funcrvalue: NAME LPAREN classnames RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :rvalue } quotedtext: DQTEXT { result = ast AST::String, :value => val[0] } | SQTEXT { result = ast AST::FlatString, :value => val[0] } boolean: BOOLEAN { result = ast AST::Boolean, :value => val[0] } objectref: name LBRACK rvalue RBRACK { result = ast AST::ObjectRef, :type => val[0], :name => val[2] } ifstatement: IF iftest LBRACE statements RBRACE else { args = { :test => val[1], :statements => val[3] } if val[5] args[:else] = val[5] end result = ast AST::IfStatement, args } else: # nothing | ELSE LBRACE statements RBRACE { result = ast AST::Else, :statements => val[2] } # Currently we only support a single value, but eventually one assumes # we'll support operators and such. iftest: rvalue casestatement: CASE rvalue LBRACE caseopts RBRACE { options = val[3] unless options.instance_of?(AST::ASTArray) options = ast AST::ASTArray, :children => [val[3]] end result = ast AST::CaseStatement, :test => val[1], :options => options } caseopts: caseopt | caseopts caseopt { if val[0].instance_of?(AST::ASTArray) val[0].push val[1] result = val[0] else result = ast AST::ASTArray, :children => [val[0], val[1]] end } caseopt: casevalues COLON LBRACE statements RBRACE { result = ast AST::CaseOpt, :value => val[0], :statements => val[3] } | casevalues COLON LBRACE RBRACE { result = ast(AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) } casevalues: selectlhand | casevalues COMMA selectlhand { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } selector: selectlhand QMARK svalues { result = ast AST::Selector, :param => val[0], :values => val[2] } svalues: selectval | LBRACE sintvalues RBRACE { result = val[1] } sintvalues: selectval | sintvalues comma selectval { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } selectval: selectlhand FARROW rvalue { result = ast AST::ObjectParam, :param => val[0], :value => val[2] } selectlhand: name | type | quotedtext | variable | funcrvalue | boolean | DEFAULT { result = ast AST::Default, :value => val[0] } import: IMPORT quotedtext { # importing files # yuk, i hate keywords # we'll probably have to have some kind of search path eventually # but for now, just use a path relative to the file doing the importing dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '') if dir == "" dir = "." end result = ast AST::ASTArray Dir.chdir(dir) { # We can't interpolate at this point since we don't have any # scopes set up. Warn the user if they use a variable reference pat = val[1].value if pat.index("$") Puppet.warning( "The import of #{pat} contains a variable reference;" + " variables are not interpolated for imports " + "in file #{@lexer.file} at line #{@lexer.line}" ) end files = Dir.glob(pat) if files.size == 0 files = Dir.glob(pat + ".pp") if files.size == 0 raise Puppet::ImportError.new("No file(s) found for import " + "of '#{pat}'") end end files.each { |file| parser = Puppet::Parser::Parser.new() parser.files = self.files Puppet.debug("importing '%s'" % file) unless file =~ /^#{File::SEPARATOR}/ file = File.join(dir, file) end begin parser.file = file rescue Puppet::ImportError Puppet.warning( "Importing %s would result in an import loop" % File.join(dir, file) ) next end # push the results into the main result array # We always return an array when we parse. parser.parse.each do |child| result.push child end } } } # Disable definition inheritance for now. 8/27/06, luke #definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE { definition: DEFINE NAME argumentlist LBRACE statements RBRACE { args = { :type => ast(AST::Name, :value => val[1]), :args => val[2], :code => val[4] # Switch to 5 for parents } if val[3].instance_of?(AST::Name) args[:parentclass] = val[3] end result = ast AST::CompDef, args #} | DEFINE NAME argumentlist parent LBRACE RBRACE { } | DEFINE NAME argumentlist LBRACE RBRACE { args = { :type => ast(AST::Name, :value => val[1]), :args => val[2], :code => ast(AST::ASTArray) } if val[3].instance_of?(AST::Name) args[:parentclass] = val[3] end result = ast AST::CompDef, args } #hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE { hostclass: CLASS NAME parent LBRACE statements RBRACE { #:args => val[2], args = { :type => ast(AST::Name, :value => val[1]), :code => val[4] } # It'll be an ASTArray if we didn't get a parent if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::ClassDef, args } | CLASS NAME parent LBRACE RBRACE { args = { :type => ast(AST::Name, :value => val[1]), :code => ast(AST::ASTArray, :children => []) } # It'll be an ASTArray if we didn't get a parent if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::ClassDef, args } nodedef: NODE hostnames parent LBRACE statements RBRACE { unless val[1].instance_of?(AST::ASTArray) val[1] = ast AST::ASTArray, :line => val[1].line, :file => val[1].file, :children => [val[1]] end args = { :names => val[1], :code => val[4] } if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::NodeDef, args } | NODE hostnames parent LBRACE RBRACE { unless val[1].instance_of?(AST::ASTArray) val[1] = ast AST::ASTArray, :line => val[1].line, :file => val[1].file, :children => [val[1]] end args = { :names => val[1], :code => ast(AST::ASTArray, :children => []) } if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::NodeDef,args } # Multiple hostnames, as used for node names. hostnames: hostname | hostnames COMMA hostname { if val[0].instance_of?(AST::ASTArray) result = val[0] result.push val[2] else result = ast AST::ASTArray, :children => [val[0], val[2]] end } hostname: NAME { result = ast AST::HostName, :value => val[0] } | SQTEXT { result = ast AST::HostName, :value => val[0] } | DEFAULT { result = ast AST::Default, :value => val[0] } nothing: { result = ast AST::ASTArray, :children => [] } argumentlist: nothing | LPAREN nothing RPAREN { result = val[1] } | LPAREN arguments RPAREN { if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end } arguments: argument | arguments COMMA argument { if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end } argument: name EQUALS rvalue { msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype" msg += " at line %s" % @lexer.line msg += " in file %s" % @lexer.file if @lexer.file Puppet.warning msg result = ast AST::CompArgument, :children => [val[0],val[2]] } | name { msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype" msg += " at line %s" % @lexer.line msg += " in file %s" % @lexer.file if @lexer.file Puppet.warning msg result = ast AST::CompArgument, :children => [val[0]] } | lvariable EQUALS rvalue { result = ast AST::CompArgument, :children => [val[0],val[2]] } | lvariable { result = ast AST::CompArgument, :children => [val[0]] } parent: nothing | INHERITS NAME { result = ast AST::Name, :value => val[1] } variable: VARIABLE { name = val[0].sub(/^\$/,'') result = ast AST::Variable, :value => name } # This is variables as lvalues; we're assigning them, not deferencing them. lvariable: VARIABLE { result = ast AST::Name, :value => val[0].sub(/^\$/,'') } array: LBRACK rvalues RBRACK { if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end } | LBRACK RBRACK { result = ast AST::ASTArray } comma: FARROW | COMMA endcomma: # nothing | COMMA { result = nil } end ---- header ---- require 'puppet' require 'puppet/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' #require 'puppet/parser/interpreter' module Puppet class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end end Puppet[:typecheck] = true Puppet[:paramcheck] = true ---- inner ---- require 'puppet/parser/functions' attr_reader :file attr_accessor :files # Create an AST array out of all of the args def aryfy(*args) if args[0].instance_of?(AST::ASTArray) result = args.shift args.each { |arg| result.push arg } else result = ast AST::ASTArray, :children => args end return result end # Create an AST object, and automatically add the file and line information if # available. def ast(klass, hash = nil) hash ||= {} unless hash[:line] hash[:line] = @lexer.line end unless hash[:file] if file = @lexer.file hash[:file] = file end end return klass.new(hash) end def file=(file) unless FileTest.exists?(file) unless file =~ /\.pp$/ file = file + ".pp" end unless FileTest.exists?(file) raise Puppet::Error, "Could not find file %s" % file end end if @files.detect { |f| f.file == file } raise Puppet::ImportError.new("Import loop detected") else @files << Puppet::LoadedFile.new(file) @lexer.file = file end end def initialize @lexer = Puppet::Parser::Lexer.new() @files = [] #if Puppet[:debug] # @yydebug = true #end end +# Add a new file to be checked when we're checking to see if we should be +# reparsed. +def newfile(*files) + files.each do |file| + unless file.is_a? Puppet::LoadedFile + file = Puppet::LoadedFile.new(file) + end + @files << file + end +end + def on_error(token,value,stack) #on '%s' at '%s' in\n'%s'" % [token,value,stack] #error = "line %s: parse error after '%s'" % # [@lexer.line,@lexer.last] error = "Syntax error at '%s'" % [value] - #if Puppet[:debug] - #puts stack.inspect - #puts stack.class - #end - #if @lexer.file - # error += (" in '%s'" % @lexer.file) - #end - except = Puppet::ParseError.new(error) except.line = @lexer.line if @lexer.file except.file = @lexer.file end raise except end # how should I do error handling here? def parse(string = nil) if string self.string = string end begin yyparse(@lexer,:scan) rescue Racc::ParseError => except error = Puppet::ParseError.new(except) error.line = @lexer.line error.file = @lexer.file error.set_backtrace except.backtrace raise error rescue Puppet::ParseError => except except.line ||= @lexer.line except.file ||= @lexer.file raise except rescue Puppet::Error => except # and this is a framework error except.line ||= @lexer.line except.file ||= @lexer.file - #if Puppet[:debug] - # puts except.stack - #end raise except rescue Puppet::DevError => except except.line ||= @lexer.line except.file ||= @lexer.file - #if Puppet[:debug] - # puts except.stack - #end raise except rescue => except error = Puppet::DevError.new(except.message) error.line = @lexer.line error.file = @lexer.file error.set_backtrace except.backtrace - #if Puppet[:debug] - # puts caller - #end raise error end end +# See if any of the files have changed. def reparse? - @files.detect { |file| - file.changed? - } + if file = @files.detect { |file| file.changed? } + return file.stamp + else + return false + end end def string=(string) @lexer.string = string end # Make emacs happy # Local Variables: # mode: ruby # End: # $Id$ diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index ad30e6ed3..26bf8104e 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -1,473 +1,466 @@ # The interepreter's job is to convert from a parsed file to the configuration # for a given client. It really doesn't do any work on its own, it just collects # and calls out to other objects. require 'puppet' require 'puppet/parser/parser' require 'puppet/parser/scope' module Puppet module Parser class Interpreter include Puppet::Util Puppet.setdefaults("ldap", :ldapnodes => [false, "Whether to search for node configurations in LDAP."], :ldapssl => [false, "Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side."], :ldaptls => [false, "Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side."], :ldapserver => ["ldap", "The LDAP server. Only used if ``ldapnodes`` is enabled."], :ldapport => [389, "The LDAP port. Only used if ``ldapnodes`` is enabled."], :ldapstring => ["(&(objectclass=puppetClient)(cn=%s))", "The search string used to find an LDAP node."], :ldapattrs => ["puppetclass", "The LDAP attributes to use to define Puppet classes. Values should be comma-separated."], :ldapparentattr => ["parentnode", "The attribute to use to define the parent node."], :ldapuser => ["", "The user to use to connect to LDAP. Must be specified as a full DN."], :ldappassword => ["", "The password to use to connect to LDAP."], :ldapbase => ["", "The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory."] ) Puppet.setdefaults(:puppetmaster, :storeconfigs => [false, "Whether to store each client's configuration. This requires ActiveRecord from Ruby on Rails."] ) attr_accessor :ast class << self attr_writer :ldap end # just shorten the constant path a bit, using what amounts to an alias AST = Puppet::Parser::AST # Create an ldap connection. This is a class method so others can call # it and use the same variables and such. def self.ldap unless defined? @ldap and @ldap if Puppet[:ldapssl] @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport]) elsif Puppet[:ldaptls] @ldap = LDAP::SSLConn.new( Puppet[:ldapserver], Puppet[:ldapport], true ) else @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport]) end @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3) @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON) @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword]) end return @ldap end # create our interpreter def initialize(hash) if @code = hash[:Code] @file = nil # to avoid warnings elsif ! @file = hash[:Manifest] raise Puppet::DevError, "You must provide code or a manifest" end - @lastchecked = 0 - if hash.include?(:UseNodes) @usenodes = hash[:UseNodes] else @usenodes = true end # By default, we only search the parse tree. @nodesources = [] if Puppet[:ldapnodes] @nodesources << :ldap end if hash[:NodeSources] hash[:NodeSources].each do |src| if respond_to? "nodesearch_#{src.to_s}" @nodesources << src.to_s.intern else Puppet.warning "Node source '#{src}' not supported" end end end @setup = false # Set it to either the value or nil. This is currently only used # by the cfengine module. @classes = hash[:Classes] || [] @local = hash[:Local] || false if hash.include?(:ForkSave) @forksave = hash[:ForkSave] else # This is just too dangerous right now. Sorry, it's going # to have to be slow. @forksave = false end if Puppet[:storeconfigs] Puppet::Rails.init end @files = [] # Create our parser object parsefiles end # Search for our node in the various locations. This only searches # locations external to the files; the scope is responsible for # searching the parse tree. def nodesearch(*nodes) # At this point, stop at the first source that defines # the node @nodesources.each do |source| method = "nodesearch_%s" % source parent = nil nodeclasses = nil if self.respond_to? method nodes.each do |node| parent, nodeclasses = self.send(method, node) if parent or (nodeclasses and !nodeclasses.empty?) Puppet.info "Found %s in %s" % [node, source] return parent, nodeclasses else # Look for a default node. parent, nodeclasses = self.send(method, "default") if parent or (nodeclasses and !nodeclasses.empty?) Puppet.info "Found default node for %s in %s" % [node, source] return parent, nodeclasses end end end end end return nil, nil end # Find the ldap node and extra the info, returning just # the critical data. def nodesearch_ldap(node) unless defined? @ldap and @ldap setup_ldap() unless @ldap Puppet.info "Skipping ldap source; no ldap connection" return nil, [] end end if node =~ /\./ node = node.sub(/\..+/, '') end filter = Puppet[:ldapstring] attrs = Puppet[:ldapattrs].split("\s*,\s*") sattrs = attrs.dup pattr = nil if pattr = Puppet[:ldapparentattr] if pattr == "" pattr = nil else sattrs << pattr end end if filter =~ /%s/ filter = filter.gsub(/%s/, node) end parent = nil classes = [] found = false count = 0 begin # We're always doing a sub here; oh well. @ldap.search(Puppet[:ldapbase], 2, filter, sattrs) do |entry| found = true if pattr if values = entry.vals(pattr) if values.length > 1 raise Puppet::Error, "Node %s has more than one parent: %s" % [node, values.inspect] end unless values.empty? parent = values.shift end end end attrs.each { |attr| if values = entry.vals(attr) values.each do |v| classes << v end end } end rescue => detail if count == 0 # Try reconnecting to ldap @ldap = nil setup_ldap() retry else raise Puppet::Error, "LDAP Search failed: %s" % detail end end classes.flatten! return parent, classes end def parsedate parsefiles() @parsedate end # Add a new file to check for updateness. def newfile(file) unless @files.find { |f| f.file == file } @files << Puppet::LoadedFile.new(file) end end # evaluate our whole tree def run(client, facts) # We have to leave this for after initialization because there # seems to be a problem keeping ldap open after a fork. unless @setup @nodesources.each { |source| method = "setup_%s" % source.to_s if respond_to? method begin self.send(method) rescue => detail raise Puppet::Error, "Could not set up node source %s" % source end end } end parsefiles() # Really, we should stick multiple names in here # but for now just make a simple array names = [client] # Make sure both the fqdn and the short name of the # host can be used in the manifest if client =~ /\./ names << client.sub(/\..+/,'') else names << "#{client}.#{facts['domain']}" end scope = Puppet::Parser::Scope.new() # no parent scope scope.name = "top" scope.type = "puppet" scope.interp = self classes = @classes.dup args = {:ast => @ast, :facts => facts, :classes => classes} if @usenodes unless client raise Puppet::Error, "Cannot evaluate nodes with a nil client" end args[:names] = names parent, nodeclasses = nodesearch(*names) args[:classes] += nodeclasses if nodeclasses args[:parentnode] = parent if parent if nodeclasses or parent args[:searched] = true end end begin objects = scope.evaluate(args) rescue Puppet::DevError, Puppet::Error, Puppet::ParseError => except raise rescue => except error = Puppet::DevError.new("%s: %s" % [except.class, except.message]) error.set_backtrace except.backtrace raise error end if Puppet[:storeconfigs] storeconfigs( :objects => objects, :host => client, :facts => facts ) end return objects end # Connect to the LDAP Server def setup_ldap self.class.ldap = nil begin require 'ldap' rescue LoadError Puppet.notice( "Could not set up LDAP Connection: Missing ruby/ldap libraries" ) @ldap = nil return end begin @ldap = self.class.ldap() rescue => detail raise Puppet::Error, "Could not connect to LDAP: %s" % detail end end def scope return @scope end private # Check whether any of our files have changed. def checkfiles if @files.find { |f| f.changed? } @parsedate = Time.now.to_i end end # Parse the files, generating our parse tree. This automatically # reparses only if files are updated, so it's safe to call multiple # times. def parsefiles # First check whether there are updates to any non-puppet files # like templates. If we need to reparse, this will get quashed, # but it needs to be done first in case there's no reparse # but there are other file changes. checkfiles() # Check if the parser should reparse. if @file if defined? @parser - unless @parser.reparse? - @lastchecked = Time.now + if stamp = @parser.reparse? + Puppet.notice "Reloading files" + else return false end end unless FileTest.exists?(@file) if @ast return else raise Puppet::Error, "Manifest %s must exist" % @file end end end - if defined? @parser - # If this isn't our first time parsing in this process, - # note that we're reparsing. - Puppet.info "Reloading files" - end # should i be creating a new parser each time...? @parser = Puppet::Parser::Parser.new() if @code @parser.string = @code else @parser.file = @file + # Mark when we parsed, so we can check freshness + @parsedate = File.stat(@file).ctime.to_i end if @local @ast = @parser.parse else benchmark(:info, "Parsed manifest") do @ast = @parser.parse end end - - # Mark when we parsed, so we can check freshness @parsedate = Time.now.to_i - @lastchecked = Time.now end # Store the configs into the database. def storeconfigs(hash) unless defined? ActiveRecord require 'puppet/rails' unless defined? ActiveRecord raise LoadError, "storeconfigs is enabled but rails is unavailable" end end Puppet::Rails.init # Fork the storage, since we don't need the client waiting # on that. How do I avoid this duplication? if @forksave fork { # We store all of the objects, even the collectable ones benchmark(:info, "Stored configuration for #{hash[:client]}") do # Try to batch things a bit, by putting them into # a transaction Puppet::Rails::Host.transaction do Puppet::Rails::Host.store(hash) end end } else # We store all of the objects, even the collectable ones benchmark(:info, "Stored configuration for #{hash[:client]}") do Puppet::Rails::Host.transaction do Puppet::Rails::Host.store(hash) end end end # Now that we've stored everything, we need to strip out # the collectable objects so that they are not sent on # to the host hash[:objects].collectstrip! end end end end # $Id$ diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index 41c119c2c..a79f18b0e 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -1,1522 +1,1519 @@ # # DO NOT MODIFY!!!! # This file is automatically generated by racc 1.4.5 # from racc grammer file "grammar.ra". # require 'racc/parser' require 'puppet' require 'puppet/loadedfile' require 'puppet/parser/lexer' require 'puppet/parser/ast' #require 'puppet/parser/interpreter' module Puppet class ParseError < Puppet::Error; end class ImportError < Racc::ParseError; end end Puppet[:typecheck] = true Puppet[:paramcheck] = true module Puppet module Parser class Parser < Racc::Parser -module_eval <<'..end grammar.ra modeval..idab2f060823', 'grammar.ra', 603 +module_eval <<'..end grammar.ra modeval..id1e49212871', 'grammar.ra', 603 require 'puppet/parser/functions' attr_reader :file attr_accessor :files # Create an AST array out of all of the args def aryfy(*args) if args[0].instance_of?(AST::ASTArray) result = args.shift args.each { |arg| result.push arg } else result = ast AST::ASTArray, :children => args end return result end # Create an AST object, and automatically add the file and line information if # available. def ast(klass, hash = nil) hash ||= {} unless hash[:line] hash[:line] = @lexer.line end unless hash[:file] if file = @lexer.file hash[:file] = file end end return klass.new(hash) end def file=(file) unless FileTest.exists?(file) unless file =~ /\.pp$/ file = file + ".pp" end unless FileTest.exists?(file) raise Puppet::Error, "Could not find file %s" % file end end if @files.detect { |f| f.file == file } raise Puppet::ImportError.new("Import loop detected") else @files << Puppet::LoadedFile.new(file) @lexer.file = file end end def initialize @lexer = Puppet::Parser::Lexer.new() @files = [] #if Puppet[:debug] # @yydebug = true #end end +# Add a new file to be checked when we're checking to see if we should be +# reparsed. +def newfile(*files) + files.each do |file| + unless file.is_a? Puppet::LoadedFile + file = Puppet::LoadedFile.new(file) + end + @files << file + end +end + def on_error(token,value,stack) #on '%s' at '%s' in\n'%s'" % [token,value,stack] #error = "line %s: parse error after '%s'" % # [@lexer.line,@lexer.last] error = "Syntax error at '%s'" % [value] - #if Puppet[:debug] - #puts stack.inspect - #puts stack.class - #end - #if @lexer.file - # error += (" in '%s'" % @lexer.file) - #end - except = Puppet::ParseError.new(error) except.line = @lexer.line if @lexer.file except.file = @lexer.file end raise except end # how should I do error handling here? def parse(string = nil) if string self.string = string end begin yyparse(@lexer,:scan) rescue Racc::ParseError => except error = Puppet::ParseError.new(except) error.line = @lexer.line error.file = @lexer.file error.set_backtrace except.backtrace raise error rescue Puppet::ParseError => except except.line ||= @lexer.line except.file ||= @lexer.file raise except rescue Puppet::Error => except # and this is a framework error except.line ||= @lexer.line except.file ||= @lexer.file - #if Puppet[:debug] - # puts except.stack - #end raise except rescue Puppet::DevError => except except.line ||= @lexer.line except.file ||= @lexer.file - #if Puppet[:debug] - # puts except.stack - #end raise except rescue => except error = Puppet::DevError.new(except.message) error.line = @lexer.line error.file = @lexer.file error.set_backtrace except.backtrace - #if Puppet[:debug] - # puts caller - #end raise error end end +# See if any of the files have changed. def reparse? - @files.detect { |file| - file.changed? - } + if file = @files.detect { |file| file.changed? } + return file.stamp + else + return false + end end def string=(string) @lexer.string = string end # Make emacs happy # Local Variables: # mode: ruby # End: # $Id$ -..end grammar.ra modeval..idab2f060823 +..end grammar.ra modeval..id1e49212871 ##### racc 1.4.5 generates ### racc_reduce_table = [ 0, 0, :racc_error, 1, 44, :_reduce_1, 1, 44, :_reduce_none, 1, 45, :_reduce_none, 2, 45, :_reduce_4, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 1, 47, :_reduce_none, 4, 55, :_reduce_16, 2, 55, :_reduce_17, 1, 59, :_reduce_none, 3, 59, :_reduce_19, 1, 60, :_reduce_none, 1, 60, :_reduce_none, 1, 60, :_reduce_none, 5, 48, :_reduce_23, 5, 48, :_reduce_24, 5, 48, :_reduce_25, 2, 49, :_reduce_26, 3, 50, :_reduce_27, 4, 69, :_reduce_28, 1, 64, :_reduce_none, 3, 64, :_reduce_30, 0, 65, :_reduce_none, 1, 65, :_reduce_none, 1, 61, :_reduce_33, 1, 68, :_reduce_34, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 1, 70, :_reduce_none, 3, 51, :_reduce_41, 0, 66, :_reduce_42, 1, 66, :_reduce_43, 3, 66, :_reduce_44, 3, 74, :_reduce_45, 1, 75, :_reduce_none, 3, 75, :_reduce_47, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 1, 73, :_reduce_none, 4, 79, :_reduce_57, 1, 63, :_reduce_58, 1, 63, :_reduce_59, 1, 77, :_reduce_60, 4, 78, :_reduce_61, 6, 53, :_reduce_62, 0, 81, :_reduce_none, 4, 81, :_reduce_64, 1, 80, :_reduce_none, 5, 52, :_reduce_66, 1, 82, :_reduce_none, 2, 82, :_reduce_68, 5, 83, :_reduce_69, 4, 83, :_reduce_70, 1, 84, :_reduce_none, 3, 84, :_reduce_72, 3, 71, :_reduce_73, 1, 86, :_reduce_none, 3, 86, :_reduce_75, 1, 88, :_reduce_none, 3, 88, :_reduce_77, 3, 87, :_reduce_78, 1, 85, :_reduce_none, 1, 85, :_reduce_none, 1, 85, :_reduce_none, 1, 85, :_reduce_none, 1, 85, :_reduce_none, 1, 85, :_reduce_none, 1, 85, :_reduce_85, 2, 54, :_reduce_86, 6, 56, :_reduce_87, 5, 56, :_reduce_88, 6, 57, :_reduce_89, 5, 57, :_reduce_90, 6, 58, :_reduce_91, 5, 58, :_reduce_92, 1, 91, :_reduce_none, 3, 91, :_reduce_94, 1, 92, :_reduce_95, 1, 92, :_reduce_96, 1, 92, :_reduce_97, 0, 46, :_reduce_98, 1, 89, :_reduce_none, 3, 89, :_reduce_100, 3, 89, :_reduce_101, 1, 93, :_reduce_none, 3, 93, :_reduce_103, 3, 94, :_reduce_104, 1, 94, :_reduce_105, 3, 94, :_reduce_106, 1, 94, :_reduce_107, 1, 90, :_reduce_none, 2, 90, :_reduce_109, 1, 62, :_reduce_110, 1, 95, :_reduce_111, 3, 72, :_reduce_112, 2, 72, :_reduce_113, 1, 76, :_reduce_none, 1, 76, :_reduce_none, 0, 67, :_reduce_none, 1, 67, :_reduce_117 ] racc_reduce_n = 118 racc_shift_n = 197 racc_action_table = [ 48, 36, 38, 81, 162, 20, 48, 36, 38, 64, 79, 161, 48, 36, 38, 86, 20, 36, 38, 75, 36, 38, 20, 37, -79, 145, 36, 38, 20, 44, 37, -82, -79, 49, 53, 44, 31, 55, 31, 49, 53, 44, 72, 55, 65, 49, 53, -81, 44, 55, 48, 36, 38, 37, 44, 145, 48, 36, 38, 37, 37, 137, 48, 36, 38, 113, 20, 139, 29, 79, 29, 33, 20, 33, -80, 79, 85, 170, 20, 44, 154, 146, 102, 49, 53, 44, 115, 55, 171, 49, 53, 44, 149, 55, 77, 49, 53, 36, 38, 55, 48, 36, 38, 36, 38, 116, 48, 36, 38, 117, 118, 88, 48, 36, 38, 179, 20, 117, 118, -81, 45, 87, 20, 112, 155, 44, -82, 158, 20, 44, 37, 44, 105, 49, 53, 44, 37, 55, 64, 49, 53, 44, 163, 55, 77, 49, 53, 85, 84, 55, 48, 36, 38, -80, 169, 72, 48, 36, 38, 172, 173, -83, 48, 36, 38, -84, 20, 178, 108, 136, 182, 77, 20, 36, 38, 112, 71, 160, 20, 44, 109, 70, 69, 49, 53, 44, 113, 55, 20, 49, 53, 44, 190, 55, 66, 49, 93, 36, 38, 55, 112, 44, 35, 28, 166, 49, 53, 36, 38, 55, 125, nil, 20, 36, 38, nil, nil, nil, nil, 36, 38, nil, 20, nil, nil, 44, nil, nil, 20, 49, 53, nil, nil, 55, 20, 44, nil, nil, nil, 49, 53, 44, nil, 55, nil, 49, 53, 44, nil, 55, nil, 49, 53, 36, 38, 55, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 20, 176, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 20, 44, nil, nil, 168, 49, 53, nil, 12, 55, 16, 19, nil, 24, 26, 20, 3, nil, 9, 175, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 196, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 153, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 185, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 189, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 195, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 148, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, 193, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, nil, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, nil, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, nil, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, nil, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, 20, 3, nil, 9, nil, 14, nil, 21, 12, nil, 16, 19, nil, 24, 26, nil, 3, nil, 9, nil, 14, nil, 21 ] racc_action_check = [ 48, 48, 48, 48, 133, 21, 86, 86, 86, 17, 39, 133, 137, 137, 137, 57, 48, 16, 16, 30, 79, 79, 86, 21, 98, 170, 45, 45, 137, 48, 170, 99, 57, 48, 48, 86, 3, 48, 75, 86, 86, 137, 30, 86, 17, 137, 137, 100, 79, 137, 69, 69, 69, 79, 45, 105, 119, 119, 119, 45, 105, 101, 172, 172, 172, 93, 69, 103, 3, 121, 75, 3, 119, 75, 92, 80, 93, 141, 172, 69, 121, 107, 65, 69, 69, 119, 80, 69, 141, 119, 119, 172, 111, 119, 112, 172, 172, 85, 85, 172, 12, 12, 12, 9, 9, 83, 14, 14, 14, 83, 83, 62, 113, 113, 113, 156, 12, 156, 156, 61, 9, 60, 14, 90, 122, 85, 59, 128, 113, 12, 85, 9, 66, 12, 12, 14, 9, 12, 68, 14, 14, 113, 135, 14, 136, 113, 113, 53, 52, 113, 173, 173, 173, 51, 140, 70, 158, 158, 158, 142, 144, 50, 64, 64, 64, 46, 173, 153, 72, 95, 161, 35, 158, 131, 131, 164, 28, 131, 64, 173, 74, 26, 24, 173, 173, 158, 77, 173, 131, 158, 158, 64, 178, 158, 19, 64, 64, 88, 88, 64, 76, 131, 6, 2, 138, 131, 131, 87, 87, 131, 87, nil, 88, 125, 125, nil, nil, nil, nil, 162, 162, nil, 87, nil, nil, 88, nil, nil, 125, 88, 88, nil, nil, 88, 162, 87, nil, nil, nil, 87, 87, 125, nil, 87, nil, 125, 125, 162, nil, 125, nil, 162, 162, 180, 180, 162, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 180, 147, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 147, 180, nil, nil, 139, 180, 180, nil, 147, 180, 147, 147, nil, 147, 147, 139, 147, nil, 147, 146, 147, nil, 147, 139, nil, 139, 139, nil, 139, 139, 146, 139, nil, 139, 194, 139, nil, 139, 146, nil, 146, 146, nil, 146, 146, 194, 146, nil, 146, 120, 146, nil, 146, 194, nil, 194, 194, nil, 194, 194, 120, 194, nil, 194, 167, 194, nil, 194, 120, nil, 120, 120, nil, 120, 120, 167, 120, nil, 120, 174, 120, nil, 120, 167, nil, 167, 167, nil, 167, 167, 174, 167, nil, 167, 192, 167, nil, 167, 174, nil, 174, 174, nil, 174, 174, 192, 174, nil, 174, 109, 174, nil, 174, 192, nil, 192, 192, nil, 192, 192, 109, 192, nil, 192, 182, 192, nil, 192, 109, nil, 109, 109, nil, 109, 109, 182, 109, nil, 109, nil, 109, nil, 109, 182, nil, 182, 182, nil, 182, 182, 84, 182, nil, 182, nil, 182, nil, 182, 84, nil, 84, 84, nil, 84, 84, 190, 84, nil, 84, nil, 84, nil, 84, 190, nil, 190, 190, nil, 190, 190, 5, 190, nil, 190, nil, 190, nil, 190, 5, nil, 5, 5, nil, 5, 5, 0, 5, nil, 5, nil, 5, nil, 5, 0, nil, 0, 0, nil, 0, 0, nil, 0, nil, 0, nil, 0, nil, 0 ] racc_action_pointer = [ 457, nil, 203, 32, nil, 442, 196, nil, nil, 100, nil, nil, 98, nil, 104, nil, 14, 3, nil, 158, nil, -13, nil, nil, 169, nil, 145, nil, 176, nil, 9, nil, nil, nil, nil, 135, nil, nil, nil, 0, nil, nil, nil, nil, nil, 23, 146, nil, -2, nil, 142, 134, 142, 127, nil, nil, nil, 13, nil, 107, 102, 100, 105, nil, 160, 40, 112, nil, 132, 48, 122, nil, 132, nil, 174, 34, 190, 177, nil, 17, 65, nil, nil, 100, 412, 94, 4, 204, 194, nil, 113, nil, 55, 56, nil, 152, nil, nil, 5, 12, 28, 24, nil, 61, nil, 24, nil, 75, nil, 382, nil, 85, 58, 110, nil, nil, nil, nil, nil, 54, 322, 59, 119, nil, nil, 210, nil, nil, 118, nil, nil, 170, nil, -6, nil, 135, 108, 10, 197, 277, 133, 67, 146, nil, 147, nil, 292, 262, nil, nil, nil, nil, nil, 140, nil, nil, 108, nil, 154, nil, nil, 164, 216, nil, 165, nil, nil, 337, nil, nil, -6, nil, 60, 148, 352, nil, nil, nil, 186, nil, 250, nil, 397, nil, nil, nil, nil, nil, nil, nil, 427, nil, 367, nil, 307, nil, nil ] racc_action_default = [ -98, -12, -118, -118, -13, -1, -118, -14, -2, -33, -15, -3, -118, -5, -118, -6, -118, -118, -7, -118, -34, -118, -8, -9, -118, -10, -118, -11, -118, -95, -98, -96, -93, -97, -4, -42, -58, -33, -59, -17, -18, -20, -21, -22, -110, -118, -51, -55, -118, -60, -56, -50, -118, -33, -52, -85, -54, -49, -65, -53, -118, -48, -118, -86, -42, -118, -98, -26, -118, -118, -98, 197, -118, -108, -118, -118, -116, -118, -43, -118, -118, -113, -46, -118, -118, -118, -118, -118, -118, -84, -116, -83, -37, -33, -29, -118, -38, -40, -36, -39, -35, -31, -27, -118, -99, -98, -41, -118, -109, -118, -94, -118, -117, -118, -19, -16, -112, -114, -115, -118, -118, -118, -118, -80, -79, -118, -82, -81, -118, -73, -74, -118, -67, -118, -71, -118, -42, -32, -118, -118, -118, -118, -105, -102, -107, -111, -118, -118, -92, -25, -44, -45, -47, -63, -57, -61, -118, -76, -118, -68, -66, -118, -118, -24, -116, -30, -23, -118, -88, -100, -118, -101, -118, -118, -118, -90, -91, -62, -118, -75, -118, -78, -118, -72, -28, -87, -103, -104, -106, -89, -118, -77, -118, -70, -118, -69, -64 ] racc_goto_table = [ 5, 76, 34, 43, 32, 8, 58, 42, 62, 111, 63, 132, 39, 143, 150, 51, 52, 51, 130, 94, 74, 128, 134, 135, 177, 96, 89, 119, 97, 131, 90, 83, 138, 129, 101, 156, 103, 114, 30, 43, 67, 141, 82, 42, 2, nil, nil, nil, 80, 89, 89, 51, nil, nil, 159, nil, 157, nil, 100, 128, 107, nil, 99, 106, nil, 134, nil, 92, nil, nil, nil, 104, 51, 43, nil, nil, 110, 42, 186, 43, 122, 127, 127, 42, 120, 126, 126, 89, 121, 51, 123, 123, 165, 89, nil, nil, 183, 184, 96, 89, 180, 97, 164, nil, nil, nil, nil, 151, nil, 147, 140, 191, nil, 152, 128, nil, 51, 34, nil, 127, nil, nil, 51, 126, 89, 127, nil, nil, 123, 126, nil, 100, nil, 41, 123, 99, 57, nil, 57, 167, 92, nil, 89, nil, 34, 68, 174, nil, nil, nil, nil, nil, 181, nil, nil, nil, 127, nil, nil, nil, 126, 51, nil, nil, 34, 123, 187, 188, nil, 41, nil, 34, 57, 91, 127, 51, 51, nil, 126, nil, nil, nil, 192, 123, nil, nil, nil, nil, 98, 34, 194, 34, nil, 57, nil, nil, 91, 91, nil, nil, nil, nil, nil, 41, nil, nil, nil, nil, nil, 41, 57, 124, 124, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 142, nil, nil, nil, nil, 91, nil, nil, 57, nil, nil, 91, nil, nil, 57, nil, nil, 91, nil, nil, 124, nil, nil, nil, nil, nil, 124, nil, nil, nil, nil, nil, 98, nil, nil, nil, nil, nil, nil, nil, nil, nil, 91, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 57, nil, nil, nil, 124, nil, nil, 91, nil, nil, nil, nil, 142, nil, 57, 57, nil, nil, nil, nil, nil, nil, 124 ] racc_goto_check = [ 2, 23, 4, 20, 49, 3, 30, 19, 30, 24, 20, 40, 16, 51, 31, 25, 37, 25, 44, 26, 47, 42, 42, 24, 38, 28, 34, 33, 29, 39, 23, 32, 22, 43, 21, 45, 46, 17, 48, 20, 5, 50, 30, 19, 1, nil, nil, nil, 16, 34, 34, 25, nil, nil, 40, nil, 44, nil, 20, 42, 47, nil, 19, 30, nil, 42, nil, 25, nil, nil, nil, 3, 25, 20, nil, nil, 49, 19, 51, 20, 30, 20, 20, 19, 2, 19, 19, 34, 16, 25, 25, 25, 26, 34, nil, nil, 42, 24, 28, 34, 33, 29, 23, nil, nil, nil, nil, 30, nil, 2, 3, 44, nil, 30, 42, nil, 25, 4, nil, 20, nil, nil, 25, 19, 34, 20, nil, nil, 25, 19, nil, 20, nil, 18, 25, 19, 18, nil, 18, 2, 25, nil, 34, nil, 4, 18, 2, nil, nil, nil, nil, nil, 30, nil, nil, nil, 20, nil, nil, nil, 19, 25, nil, nil, 4, 25, 30, 30, nil, 18, nil, 4, 18, 36, 20, 25, 25, nil, 19, nil, nil, nil, 2, 25, nil, nil, nil, nil, 18, 4, 2, 4, nil, 18, nil, nil, 36, 36, nil, nil, nil, nil, nil, 18, nil, nil, nil, nil, nil, 18, 18, 18, 18, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 18, nil, nil, nil, nil, 36, nil, nil, 18, nil, nil, 36, nil, nil, 18, nil, nil, 36, nil, nil, 18, nil, nil, nil, nil, nil, 18, nil, nil, nil, nil, nil, 18, nil, nil, nil, nil, nil, nil, nil, nil, nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 18, nil, nil, nil, 18, nil, nil, 36, nil, nil, nil, nil, 18, nil, 18, 18, nil, nil, nil, nil, nil, nil, 18 ] racc_goto_pointer = [ nil, 44, 0, 5, -3, 19, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 3, -42, 124, -2, -6, -30, -69, -34, -67, 3, -45, nil, -39, -36, -6, -98, -17, -56, -38, nil, 109, 4, -129, -59, -77, nil, -66, -54, -69, -90, -30, -10, 35, 1, -64, -92, nil ] racc_goto_default = [ nil, nil, nil, 73, 11, 13, 15, 18, 22, 23, 25, 27, 1, 4, 7, 10, nil, 40, 17, 59, 61, nil, nil, nil, nil, 6, nil, 95, 54, 56, nil, 78, nil, nil, 46, 47, 50, nil, nil, nil, nil, 133, 60, nil, nil, nil, nil, nil, nil, nil, nil, nil, 144 ] racc_token_table = { false => 0, Object.new => 1, :LBRACK => 2, :DQTEXT => 3, :SQTEXT => 4, :RBRACK => 5, :LBRACE => 6, :RBRACE => 7, :SYMBOL => 8, :FARROW => 9, :COMMA => 10, :TRUE => 11, :FALSE => 12, :EQUALS => 13, :LESSEQUAL => 14, :NOTEQUAL => 15, :DOT => 16, :COLON => 17, :TYPE => 18, :QMARK => 19, :LPAREN => 20, :RPAREN => 21, :ISEQUAL => 22, :GREATEREQUAL => 23, :GREATERTHAN => 24, :LESSTHAN => 25, :IF => 26, :ELSE => 27, :IMPORT => 28, :DEFINE => 29, :ELSIF => 30, :VARIABLE => 31, :CLASS => 32, :INHERITS => 33, :NODE => 34, :BOOLEAN => 35, :NAME => 36, :SEMIC => 37, :CASE => 38, :DEFAULT => 39, :AT => 40, :LCOLLECT => 41, :RCOLLECT => 42 } racc_use_result_var = true racc_nt_base = 43 Racc_arg = [ racc_action_table, racc_action_check, racc_action_default, racc_action_pointer, racc_goto_table, racc_goto_check, racc_goto_default, racc_goto_pointer, racc_nt_base, racc_reduce_table, racc_token_table, racc_shift_n, racc_reduce_n, racc_use_result_var ] Racc_token_to_s_table = [ '$end', 'error', 'LBRACK', 'DQTEXT', 'SQTEXT', 'RBRACK', 'LBRACE', 'RBRACE', 'SYMBOL', 'FARROW', 'COMMA', 'TRUE', 'FALSE', 'EQUALS', 'LESSEQUAL', 'NOTEQUAL', 'DOT', 'COLON', 'TYPE', 'QMARK', 'LPAREN', 'RPAREN', 'ISEQUAL', 'GREATEREQUAL', 'GREATERTHAN', 'LESSTHAN', 'IF', 'ELSE', 'IMPORT', 'DEFINE', 'ELSIF', 'VARIABLE', 'CLASS', 'INHERITS', 'NODE', 'BOOLEAN', 'NAME', 'SEMIC', 'CASE', 'DEFAULT', 'AT', 'LCOLLECT', 'RCOLLECT', '$start', 'program', 'statements', 'nothing', 'statement', 'object', 'collectable', 'collection', 'assignment', 'casestatement', 'ifstatement', 'import', 'fstatement', 'definition', 'hostclass', 'nodedef', 'classnames', 'classname', 'name', 'variable', 'quotedtext', 'objectinstances', 'endsemi', 'params', 'endcomma', 'type', 'objectinst', 'objectname', 'selector', 'array', 'rvalue', 'param', 'rvalues', 'comma', 'boolean', 'objectref', 'funcrvalue', 'iftest', 'else', 'caseopts', 'caseopt', 'casevalues', 'selectlhand', 'svalues', 'selectval', 'sintvalues', 'argumentlist', 'parent', 'hostnames', 'hostname', 'arguments', 'argument', 'lvariable'] Racc_debug_parser = false ##### racc system variables end ##### # reduce 0 omitted module_eval <<'.,.,', 'grammar.ra', 24 def _reduce_1( val, _values, result ) # Make sure we always return an array. if val[0].is_a?(AST::ASTArray) result = val[0] else result = aryfy(val[0]) end result end .,., # reduce 2 omitted # reduce 3 omitted module_eval <<'.,.,', 'grammar.ra', 35 def _reduce_4( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[1]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[1]] end result end .,., # reduce 5 omitted # reduce 6 omitted # reduce 7 omitted # reduce 8 omitted # reduce 9 omitted # reduce 10 omitted # reduce 11 omitted # reduce 12 omitted # reduce 13 omitted # reduce 14 omitted # reduce 15 omitted module_eval <<'.,.,', 'grammar.ra', 56 def _reduce_16( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement result end .,., module_eval <<'.,.,', 'grammar.ra', 63 def _reduce_17( val, _values, result ) args = aryfy(val[1]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :statement result end .,., # reduce 18 omitted module_eval <<'.,.,', 'grammar.ra', 82 def _reduce_19( val, _values, result ) result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file result end .,., # reduce 20 omitted # reduce 21 omitted # reduce 22 omitted module_eval <<'.,.,', 'grammar.ra', 111 def _reduce_23( val, _values, result ) if val[0].instance_of?(AST::ASTArray) raise Puppet::ParseError, "Invalid name" end array = val[2] if array.instance_of?(AST::ObjectInst) array = [array] end result = ast AST::ASTArray # this iterates across each specified objectinstance array.each { |instance| unless instance.instance_of?(AST::ObjectInst) raise Puppet::Dev, "Got something that isn't an instance" end # now, i need to somehow differentiate between those things with # arrays in their names, and normal things result.push ast(AST::ObjectDef, :type => val[0], :name => instance[0], :params => instance[1]) } result end .,., module_eval <<'.,.,', 'grammar.ra', 120 def _reduce_24( val, _values, result ) if val[0].instance_of?(AST::ASTArray) Puppet.notice "invalid name" raise Puppet::ParseError, "Invalid name" end # an object but without a name # this cannot be an instance of a library type result = ast AST::ObjectDef, :type => val[0], :params => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 126 def _reduce_25( val, _values, result ) # a template setting for a type if val[0].instance_of?(AST::ASTArray) raise Puppet::ParseError, "Invalid type" end result = ast(AST::TypeDefaults, :type => val[0], :params => val[2]) result end .,., module_eval <<'.,.,', 'grammar.ra', 149 def _reduce_26( val, _values, result ) unless Puppet[:storeconfigs] raise Puppet::ParseError, "You cannot collect without storeconfigs being set" end if val[1].is_a? AST::TypeDefaults raise Puppet::ParseError, "Defaults are not collectable" end # Just mark our objects as collectable and pass them through. if val[1].instance_of?(AST::ASTArray) val[1].each do |obj| obj.collectable = true end else val[1].collectable = true end result = val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 158 def _reduce_27( val, _values, result ) unless Puppet[:storeconfigs] raise Puppet::ParseError, "You cannot collect without storeconfigs being set" end result = ast AST::Collection, :type => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 162 def _reduce_28( val, _values, result ) result = ast AST::ObjectInst, :children => [val[0],val[2]] result end .,., # reduce 29 omitted module_eval <<'.,.,', 'grammar.ra', 172 def _reduce_30( val, _values, result ) if val[0].instance_of?(AST::ObjectInst) result = ast AST::ASTArray, :children => [val[0],val[2]] else val[0].push val[2] result = val[0] end result end .,., # reduce 31 omitted # reduce 32 omitted module_eval <<'.,.,', 'grammar.ra', 179 def _reduce_33( val, _values, result ) result = ast AST::Name, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 183 def _reduce_34( val, _values, result ) result = ast AST::Type, :value => val[0] result end .,., # reduce 35 omitted # reduce 36 omitted # reduce 37 omitted # reduce 38 omitted # reduce 39 omitted # reduce 40 omitted module_eval <<'.,.,', 'grammar.ra', 196 def _reduce_41( val, _values, result ) # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0].sub(/^\$/,'') result = ast AST::VarDef, :name => variable, :value => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 201 def _reduce_42( val, _values, result ) result = ast AST::ASTArray result end .,., module_eval <<'.,.,', 'grammar.ra', 201 def _reduce_43( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 210 def _reduce_44( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 215 def _reduce_45( val, _values, result ) leaf = ast AST::String, :value => val[0] result = ast AST::ObjectParam, :param => leaf, :value => val[2] result end .,., # reduce 46 omitted module_eval <<'.,.,', 'grammar.ra', 224 def _reduce_47( val, _values, result ) if val[0].instance_of?(AST::ASTArray) result = val[0].push(val[2]) else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., # reduce 48 omitted # reduce 49 omitted # reduce 50 omitted # reduce 51 omitted # reduce 52 omitted # reduce 53 omitted # reduce 54 omitted # reduce 55 omitted # reduce 56 omitted module_eval <<'.,.,', 'grammar.ra', 243 def _reduce_57( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0], :arguments => args, :ftype => :rvalue result end .,., module_eval <<'.,.,', 'grammar.ra', 247 def _reduce_58( val, _values, result ) result = ast AST::String, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 249 def _reduce_59( val, _values, result ) result = ast AST::FlatString, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 253 def _reduce_60( val, _values, result ) result = ast AST::Boolean, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 257 def _reduce_61( val, _values, result ) result = ast AST::ObjectRef, :type => val[0], :name => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 270 def _reduce_62( val, _values, result ) args = { :test => val[1], :statements => val[3] } if val[5] args[:else] = val[5] end result = ast AST::IfStatement, args result end .,., # reduce 63 omitted module_eval <<'.,.,', 'grammar.ra', 275 def _reduce_64( val, _values, result ) result = ast AST::Else, :statements => val[2] result end .,., # reduce 65 omitted module_eval <<'.,.,', 'grammar.ra', 287 def _reduce_66( val, _values, result ) options = val[3] unless options.instance_of?(AST::ASTArray) options = ast AST::ASTArray, :children => [val[3]] end result = ast AST::CaseStatement, :test => val[1], :options => options result end .,., # reduce 67 omitted module_eval <<'.,.,', 'grammar.ra', 297 def _reduce_68( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push val[1] result = val[0] else result = ast AST::ASTArray, :children => [val[0], val[1]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 301 def _reduce_69( val, _values, result ) result = ast AST::CaseOpt, :value => val[0], :statements => val[3] result end .,., module_eval <<'.,.,', 'grammar.ra', 306 def _reduce_70( val, _values, result ) result = ast(AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) result end .,., # reduce 71 omitted module_eval <<'.,.,', 'grammar.ra', 316 def _reduce_72( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 320 def _reduce_73( val, _values, result ) result = ast AST::Selector, :param => val[0], :values => val[2] result end .,., # reduce 74 omitted module_eval <<'.,.,', 'grammar.ra', 322 def _reduce_75( val, _values, result ) result = val[1] result end .,., # reduce 76 omitted module_eval <<'.,.,', 'grammar.ra', 333 def _reduce_77( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 337 def _reduce_78( val, _values, result ) result = ast AST::ObjectParam, :param => val[0], :value => val[2] result end .,., # reduce 79 omitted # reduce 80 omitted # reduce 81 omitted # reduce 82 omitted # reduce 83 omitted # reduce 84 omitted module_eval <<'.,.,', 'grammar.ra', 347 def _reduce_85( val, _values, result ) result = ast AST::Default, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 404 def _reduce_86( val, _values, result ) # importing files # yuk, i hate keywords # we'll probably have to have some kind of search path eventually # but for now, just use a path relative to the file doing the importing dir = @lexer.file.sub(%r{[^/]+$},'').sub(/\/$/, '') if dir == "" dir = "." end result = ast AST::ASTArray Dir.chdir(dir) { # We can't interpolate at this point since we don't have any # scopes set up. Warn the user if they use a variable reference pat = val[1].value if pat.index("$") Puppet.warning( "The import of #{pat} contains a variable reference;" + " variables are not interpolated for imports " + "in file #{@lexer.file} at line #{@lexer.line}" ) end files = Dir.glob(pat) if files.size == 0 files = Dir.glob(pat + ".pp") if files.size == 0 raise Puppet::ImportError.new("No file(s) found for import " + "of '#{pat}'") end end files.each { |file| parser = Puppet::Parser::Parser.new() parser.files = self.files Puppet.debug("importing '%s'" % file) unless file =~ /^#{File::SEPARATOR}/ file = File.join(dir, file) end begin parser.file = file rescue Puppet::ImportError Puppet.warning( "Importing %s would result in an import loop" % File.join(dir, file) ) next end # push the results into the main result array # We always return an array when we parse. parser.parse.each do |child| result.push child end } } result end .,., module_eval <<'.,.,', 'grammar.ra', 420 def _reduce_87( val, _values, result ) args = { :type => ast(AST::Name, :value => val[1]), :args => val[2], :code => val[4] # Switch to 5 for parents } if val[3].instance_of?(AST::Name) args[:parentclass] = val[3] end result = ast AST::CompDef, args #} | DEFINE NAME argumentlist parent LBRACE RBRACE { result end .,., module_eval <<'.,.,', 'grammar.ra', 432 def _reduce_88( val, _values, result ) args = { :type => ast(AST::Name, :value => val[1]), :args => val[2], :code => ast(AST::ASTArray) } if val[3].instance_of?(AST::Name) args[:parentclass] = val[3] end result = ast AST::CompDef, args result end .,., module_eval <<'.,.,', 'grammar.ra', 446 def _reduce_89( val, _values, result ) #:args => val[2], args = { :type => ast(AST::Name, :value => val[1]), :code => val[4] } # It'll be an ASTArray if we didn't get a parent if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::ClassDef, args result end .,., module_eval <<'.,.,', 'grammar.ra', 456 def _reduce_90( val, _values, result ) args = { :type => ast(AST::Name, :value => val[1]), :code => ast(AST::ASTArray, :children => []) } # It'll be an ASTArray if we didn't get a parent if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::ClassDef, args result end .,., module_eval <<'.,.,', 'grammar.ra', 473 def _reduce_91( val, _values, result ) unless val[1].instance_of?(AST::ASTArray) val[1] = ast AST::ASTArray, :line => val[1].line, :file => val[1].file, :children => [val[1]] end args = { :names => val[1], :code => val[4] } if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::NodeDef, args result end .,., module_eval <<'.,.,', 'grammar.ra', 488 def _reduce_92( val, _values, result ) unless val[1].instance_of?(AST::ASTArray) val[1] = ast AST::ASTArray, :line => val[1].line, :file => val[1].file, :children => [val[1]] end args = { :names => val[1], :code => ast(AST::ASTArray, :children => []) } if val[2].instance_of?(AST::Name) args[:parentclass] = val[2] end result = ast AST::NodeDef,args result end .,., # reduce 93 omitted module_eval <<'.,.,', 'grammar.ra', 499 def _reduce_94( val, _values, result ) if val[0].instance_of?(AST::ASTArray) result = val[0] result.push val[2] else result = ast AST::ASTArray, :children => [val[0], val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 503 def _reduce_95( val, _values, result ) result = ast AST::HostName, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 505 def _reduce_96( val, _values, result ) result = ast AST::HostName, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 507 def _reduce_97( val, _values, result ) result = ast AST::Default, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 511 def _reduce_98( val, _values, result ) result = ast AST::ASTArray, :children => [] result end .,., # reduce 99 omitted module_eval <<'.,.,', 'grammar.ra', 516 def _reduce_100( val, _values, result ) result = val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 523 def _reduce_101( val, _values, result ) if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end result end .,., # reduce 102 omitted module_eval <<'.,.,', 'grammar.ra', 533 def _reduce_103( val, _values, result ) if val[0].instance_of?(AST::ASTArray) val[0].push(val[2]) result = val[0] else result = ast AST::ASTArray, :children => [val[0],val[2]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 541 def _reduce_104( val, _values, result ) msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype" msg += " at line %s" % @lexer.line msg += " in file %s" % @lexer.file if @lexer.file Puppet.warning msg result = ast AST::CompArgument, :children => [val[0],val[2]] result end .,., module_eval <<'.,.,', 'grammar.ra', 548 def _reduce_105( val, _values, result ) msg = "Deprecation notice: #{val[0].value} must now include '$' in prototype" msg += " at line %s" % @lexer.line msg += " in file %s" % @lexer.file if @lexer.file Puppet.warning msg result = ast AST::CompArgument, :children => [val[0]] result end .,., module_eval <<'.,.,', 'grammar.ra', 550 def _reduce_106( val, _values, result ) result = ast AST::CompArgument, :children => [val[0],val[2]] result end .,., module_eval <<'.,.,', 'grammar.ra', 552 def _reduce_107( val, _values, result ) result = ast AST::CompArgument, :children => [val[0]] result end .,., # reduce 108 omitted module_eval <<'.,.,', 'grammar.ra', 557 def _reduce_109( val, _values, result ) result = ast AST::Name, :value => val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 562 def _reduce_110( val, _values, result ) name = val[0].sub(/^\$/,'') result = ast AST::Variable, :value => name result end .,., module_eval <<'.,.,', 'grammar.ra', 567 def _reduce_111( val, _values, result ) result = ast AST::Name, :value => val[0].sub(/^\$/,'') result end .,., module_eval <<'.,.,', 'grammar.ra', 575 def _reduce_112( val, _values, result ) if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end result end .,., module_eval <<'.,.,', 'grammar.ra', 577 def _reduce_113( val, _values, result ) result = ast AST::ASTArray result end .,., # reduce 114 omitted # reduce 115 omitted # reduce 116 omitted module_eval <<'.,.,', 'grammar.ra', 582 def _reduce_117( val, _values, result ) result = nil result end .,., def _reduce_none( val, _values, result ) result end end # class Parser end # module Parser end # module Puppet diff --git a/test/language/interpreter.rb b/test/language/interpreter.rb index 51ee1e5f4..36fd5922b 100755 --- a/test/language/interpreter.rb +++ b/test/language/interpreter.rb @@ -1,352 +1,399 @@ #!/usr/bin/ruby if __FILE__ == $0 $:.unshift '../../lib' $:.unshift '..' $puppetbase = "../.." end require 'facter' require 'puppet' require 'puppet/parser/interpreter' require 'puppet/parser/parser' require 'puppet/client' require 'puppet/rails' require 'test/unit' require 'puppettest' class TestInterpreter < Test::Unit::TestCase include TestPuppet include ServerTest AST = Puppet::Parser::AST # create a simple manifest that uses nodes to create a file def mknodemanifest(node, file) createdfile = tempfile() File.open(file, "w") { |f| f.puts "node %s { file { \"%s\": ensure => file, mode => 755 } }\n" % [node, createdfile] } return [file, createdfile] end def test_simple file = tempfile() File.open(file, "w") { |f| f.puts "file { \"/etc\": owner => root }" } assert_nothing_raised { Puppet::Parser::Interpreter.new(:Manifest => file) } end def test_reloadfiles hostname = Facter["hostname"].value file = tempfile() # Create a first version createdfile = mknodemanifest(hostname, file) interp = nil assert_nothing_raised { interp = Puppet::Parser::Interpreter.new(:Manifest => file) } config = nil assert_nothing_raised { config = interp.run(hostname, {}) } sleep(1) # Now create a new file createdfile = mknodemanifest(hostname, file) newconfig = nil assert_nothing_raised { newconfig = interp.run(hostname, {}) } assert(config != newconfig, "Configs are somehow the same") end if defined? ActiveRecord def test_hoststorage assert_nothing_raised { Puppet[:storeconfigs] = true } file = tempfile() File.open(file, "w") { |f| f.puts "file { \"/etc\": owner => root }" } interp = nil assert_nothing_raised { interp = Puppet::Parser::Interpreter.new( :Manifest => file, :UseNodes => false, :ForkSave => false ) } facts = {} Facter.each { |fact, val| facts[fact] = val } objects = nil assert_nothing_raised { objects = interp.run(facts["hostname"], facts) } obj = Puppet::Rails::Host.find_by_name(facts["hostname"]) assert(obj, "Could not find host object") end else $stderr.puts "No ActiveRecord -- skipping collection tests" end if Facter["domain"].value == "madstop.com" begin require 'ldap' $haveldap = true rescue LoadError $stderr.puts "Missing ldap; skipping ldap source tests" $haveldap = false end # Only test ldap stuff on luke's network, since that's the only place we # have data for. if $haveldap def ldapconnect @ldap = LDAP::Conn.new("ldap", 389) @ldap.set_option( LDAP::LDAP_OPT_PROTOCOL_VERSION, 3 ) @ldap.simple_bind("", "") return @ldap end def ldaphost(node) parent = nil classes = nil @ldap.search( "ou=hosts, dc=madstop, dc=com", 2, "(&(objectclass=puppetclient)(cn=%s))" % node ) do |entry| parent = entry.vals("parentnode").shift classes = entry.vals("puppetclass") || [] end return parent, classes end def test_ldapnodes Puppet[:ldapbase] = "ou=hosts, dc=madstop, dc=com" Puppet[:ldapnodes] = true ldapconnect() file = tempfile() files = [] parentfile = tempfile() + "-parent" files << parentfile hostname = Facter["hostname"].value lparent, lclasses = ldaphost(Facter["hostname"].value) assert(lclasses, "Did not retrieve info from ldap") File.open(file, "w") { |f| f.puts "node #{lparent} { file { \"#{parentfile}\": ensure => file } }" lclasses.each { |klass| kfile = tempfile() + "-klass" files << kfile f.puts "class #{klass} { file { \"#{kfile}\": ensure => file } }" } } interp = nil assert_nothing_raised { interp = Puppet::Parser::Interpreter.new( :Manifest => file ) } parent = nil classes = nil # First make sure we get the default node for unknown hosts dparent, dclasses = ldaphost("default") assert_nothing_raised { parent, classes = interp.nodesearch("nosuchhostokay") } assert_equal(dparent, parent, "Default parent node did not match") assert_equal(dclasses, classes, "Default parent class list did not match") # Look for a host we know doesn't have a parent npparent, npclasses = ldaphost("noparent") assert_nothing_raised { #parent, classes = interp.nodesearch_ldap("noparent") parent, classes = interp.nodesearch("noparent") } assert_equal(npparent, parent, "Parent node did not match") assert_equal(npclasses, classes, "Class list did not match") # Now look for our normal host assert_nothing_raised { parent, classes = interp.nodesearch_ldap(hostname) } assert_equal(lparent, parent, "Parent node did not match") assert_equal(lclasses, classes, "Class list did not match") objects = nil assert_nothing_raised { objects = interp.run(hostname, Puppet::Client::MasterClient.facts) } comp = nil assert_nothing_raised { comp = objects.to_type } assert_apply(comp) files.each { |cfile| @@tmpfiles << cfile assert(FileTest.exists?(cfile), "Did not make %s" % cfile) } end if Process.uid == 0 and Facter["hostname"].value == "culain" def test_ldapreconnect Puppet[:ldapbase] = "ou=hosts, dc=madstop, dc=com" Puppet[:ldapnodes] = true interp = nil assert_nothing_raised { interp = Puppet::Parser::Interpreter.new( :Manifest => mktestmanifest() ) } hostname = "culain.madstop.com" # look for our host assert_nothing_raised { parent, classes = interp.nodesearch_ldap(hostname) } # Now restart ldap system("/etc/init.d/slapd restart 2>/dev/null >/dev/null") sleep(1) # and look again assert_nothing_raised { parent, classes = interp.nodesearch_ldap(hostname) } # Now stop ldap system("/etc/init.d/slapd stop 2>/dev/null >/dev/null") cleanup do system("/etc/init.d/slapd start 2>/dev/null >/dev/null") end # And make sure we actually fail here assert_raise(Puppet::Error) { parent, classes = interp.nodesearch_ldap(hostname) } end else $stderr.puts "Run as root for ldap reconnect tests" end end else $stderr.puts "Not in madstop.com; skipping ldap tests" end # Make sure searchnode behaves as we expect. def test_nodesearch # First create a fake nodesearch algorithm i = 0 bucket = [] Puppet::Parser::Interpreter.send(:define_method, "nodesearch_fake") do |node| return nil, nil if node == "default" return bucket[0], bucket[1] end text = %{ node nodeparent {} node othernodeparent {} class nodeclass {} class nothernode {} } manifest = tempfile() File.open(manifest, "w") do |f| f.puts text end interp = nil assert_nothing_raised { interp = Puppet::Parser::Interpreter.new( :Manifest => manifest, :NodeSources => [:fake] ) } # Make sure it behaves correctly for all forms [[nil, nil], ["nodeparent", nil], [nil, ["nodeclass"]], [nil, ["nodeclass", "nothernode"]], ["othernodeparent", ["nodeclass", "nothernode"]],].each do |ary| # Set the return values bucket = ary # Look them back up parent, classes = interp.nodesearch("mynode") # Basically, just make sure that if we have either or both, # we get a result back. assert_equal(ary[0], parent, "Parent is not %s" % parent) assert_equal(ary[1], classes, "Parent is not %s" % parent) next if ary == [nil, nil] # Now make sure we actually get the configuration. This will throw # an exception if we don't. assert_nothing_raised do interp.run("mynode", {}) end end end # Make sure nodesearch uses all names, not just one. def test_nodesearch_multiple_names bucket = {} Puppet::Parser::Interpreter.send(:define_method, "nodesearch_multifake") do |node| if bucket[node] return *bucket[node] else return nil, nil end end manifest = tempfile() File.open(manifest, "w") do |f| f.puts "" end interp = nil assert_nothing_raised { interp = Puppet::Parser::Interpreter.new( :Manifest => manifest, :NodeSources => [:multifake] ) } bucket["name.domain.com"] = [:parent, [:classes]] ret = nil assert_nothing_raised do assert_equal bucket["name.domain.com"], interp.nodesearch("name", "name.domain.com") end end + + def test_parsedate + Puppet[:filetimeout] = 0 + main = tempfile() + sub = tempfile() + mainfile = tempfile() + subfile = tempfile() + count = 0 + updatemain = proc do + count += 1 + File.open(main, "w") { |f| + f.puts "import '#{sub}' + file { \"#{mainfile}\": content => #{count} } + " + } + end + updatesub = proc do + count += 1 + File.open(sub, "w") { |f| + f.puts "file { \"#{subfile}\": content => #{count} } + " + } + end + + updatemain.call + updatesub.call + + interp = Puppet::Parser::Interpreter.new( + :Manifest => main, + :Local => true + ) + + date = interp.parsedate + + # Now update the site file and make sure we catch it + sleep 1 + updatemain.call + newdate = interp.parsedate + assert(date != newdate, "Parsedate was not updated") + date = newdate + + # And then the subfile + sleep 1 + updatesub.call + newdate = interp.parsedate + assert(date != newdate, "Parsedate was not updated") + end end diff --git a/test/other/loadedfile.rb b/test/other/loadedfile.rb index efb17a1e5..1089402a1 100755 --- a/test/other/loadedfile.rb +++ b/test/other/loadedfile.rb @@ -1,58 +1,87 @@ if __FILE__ == $0 $:.unshift '..' $:.unshift '../../lib' $puppetbase = "../.." end require 'puppet' require 'puppet/loadedfile' require 'puppettest' require 'test/unit' class TestLoadedFile < Test::Unit::TestCase include TestPuppet def test_file Puppet[:filetimeout] = 0 file = nil path = tempfile() File.open(path, "w") { |f| f.puts "yayness" } assert_nothing_raised { file = Puppet::LoadedFile.new(path) } assert(!file.changed?, "File incorrectly returned changed") - #sleep(1) File.open(path, "w") { |f| f.puts "booness" } - file.send("tstamp=".intern, File.stat(path).ctime - 5) + #file.tstamp = File.stat(path).ctime - 5 + new = File.stat(path).ctime - 5 + file.tstamp = new assert(file.changed?, "File did not catch change") end def test_timeout Puppet[:filetimeout] = 50 path = tempfile() File.open(path, "w") { |f| f.puts "yay" } file = nil assert_nothing_raised { file = Puppet::LoadedFile.new(path) } - assert_nothing_raised { file.changed? } + assert_nothing_raised { + assert(!file.changed?, + "File thought it changed immediately") + } + sleep 1 File.open(path, "w") { |f| f.puts "yay" } - file.send("tstamp=".intern, File.stat(path).ctime - 5) + #file.tstamp = File.stat(path).ctime - 5 assert(!file.changed?, "File was marked as changed too soon") Puppet[:filetimeout] = 0 assert(file.changed?, "File was not marked as changed soon enough") + end + def test_stamp + file = tempfile() + File.open(file, "w") { |f| f.puts "" } + obj = nil + assert_nothing_raised { + obj = Puppet::LoadedFile.new(file) + } + # Make sure we don't refresh + Puppet[:filetimeout] = 50 + + stamp = File.stat(file).ctime + + assert_equal(stamp, obj.stamp) + + sleep 1 + # Now change the file, and make sure the stamp doesn't update yet + File.open(file, "w") { |f| f.puts "" } + assert_equal(stamp, obj.stamp, + "File prematurely refreshed") + + Puppet[:filetimeout] = 0 + assert_equal(File.stat(file).ctime, obj.stamp, + "File did not refresh") end end # $Id$