diff --git a/lib/puppet.rb b/lib/puppet.rb index 3c5eebb0c..f53e664be 100644 --- a/lib/puppet.rb +++ b/lib/puppet.rb @@ -1,163 +1,163 @@ # Try to load rubygems. Hey rubygems, I hate you. begin require 'rubygems' rescue LoadError end # see the bottom of the file for further inclusions require 'singleton' require 'facter' require 'puppet/error' require 'puppet/util' require 'puppet/util/autoload' require 'puppet/util/settings' require 'puppet/util/feature' require 'puppet/util/suidmanager' #------------------------------------------------------------ # the top-level module # # all this really does is dictate how the whole system behaves, through # preferences for things like debugging # # it's also a place to find top-level commands like 'debug' module Puppet PUPPETVERSION = '0.25.4' def Puppet.version return PUPPETVERSION end class << self include Puppet::Util attr_reader :features attr_writer :name end # the hash that determines how our system behaves @@settings = Puppet::Util::Settings.new # The services running in this process. @services ||= [] require 'puppet/util/logging' extend Puppet::Util::Logging # The feature collection @features = Puppet::Util::Feature.new('puppet/feature') # Load the base features. require 'puppet/feature/base' # Store a new default value. def self.setdefaults(section, hash) @@settings.setdefaults(section, hash) end # configuration parameter access and stuff def self.[](param) case param when :debug if Puppet::Util::Log.level == :debug return true else return false end else return @@settings[param] end end # configuration parameter access and stuff def self.[]=(param,value) @@settings[param] = value end def self.clear @@settings.clear end def self.debug=(value) if value Puppet::Util::Log.level=(:debug) else Puppet::Util::Log.level=(:notice) end end def self.settings @@settings end # Load all of the configuration parameters. require 'puppet/defaults' def self.genmanifest if Puppet[:genmanifest] puts Puppet.settings.to_manifest exit(0) end end # Parse the config file for this process. def self.parse_config Puppet.settings.parse end # XXX this should all be done using puppet objects, not using # normal mkdir def self.recmkdir(dir,mode = 0755) if FileTest.exist?(dir) return false else tmp = dir.sub(/^\//,'') path = [File::SEPARATOR] tmp.split(File::SEPARATOR).each { |dir| path.push dir if ! FileTest.exist?(File.join(path)) begin Dir.mkdir(File.join(path), mode) rescue Errno::EACCES => detail Puppet.err detail.to_s return false rescue => detail Puppet.err "Could not create %s: %s" % [path, detail.to_s] return false end elsif FileTest.directory?(File.join(path)) next else FileTest.exist?(File.join(path)) raise Puppet::Error, "Cannot create %s: basedir %s is a file" % [dir, File.join(path)] end } return true end end # Create a new type. Just proxy to the Type class. def self.newtype(name, options = {}, &block) Puppet::Type.newtype(name, options, &block) end # Retrieve a type by name. Just proxy to the Type class. def self.type(name) # LAK:DEP Deprecation notice added 12/17/2008 Puppet.warning "Puppet.type is deprecated; use Puppet::Type.type" Puppet::Type.type(name) end end require 'puppet/type' +require 'puppet/parser' require 'puppet/network' require 'puppet/ssl' require 'puppet/module' require 'puppet/util/storage' -require 'puppet/parser/interpreter' if Puppet[:storeconfigs] require 'puppet/rails' end diff --git a/lib/puppet/application/main.rb b/lib/puppet/application/main.rb index 5eab81ae6..3813df612 100644 --- a/lib/puppet/application/main.rb +++ b/lib/puppet/application/main.rb @@ -1,183 +1,183 @@ require 'puppet' require 'puppet/application' require 'puppet/configurer' require 'puppet/network/handler' require 'puppet/network/client' Puppet::Application.new(:main) do should_parse_config option("--debug","-d") option("--execute EXECUTE","-e") do |arg| options[:code] = arg end option("--loadclasses","-L") option("--verbose","-v") option("--use-nodes") option("--detailed-exitcodes") option("--apply catalog", "-a catalog") do |arg| options[:catalog] = arg end option("--logdest LOGDEST", "-l") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:logset] = true rescue => detail $stderr.puts detail.to_s end end dispatch do if options[:catalog] :apply elsif Puppet[:parseonly] :parseonly else :main end end command(:apply) do require 'puppet/configurer' if options[:catalog] == "-" text = $stdin.read else text = File.read(options[:catalog]) end begin catalog = Puppet::Resource::Catalog.convert_from(Puppet::Resource::Catalog.default_format,text) unless catalog.is_a?(Puppet::Resource::Catalog) catalog = Puppet::Resource::Catalog.pson_create(catalog) end rescue => detail raise Puppet::Error, "Could not deserialize catalog from pson: %s" % detail end catalog = catalog.to_ral configurer = Puppet::Configurer.new configurer.run :catalog => catalog end command(:parseonly) do # Set our code or file to use. if options[:code] or ARGV.length == 0 Puppet[:code] = options[:code] || STDIN.read else Puppet[:manifest] = ARGV.shift end begin - Puppet::Parser::Interpreter.new.parser(Puppet[:environment]) + Puppet::Parser::ResourceTypeCollection.new(Puppet[:environment]).perform_initial_import rescue => detail Puppet.err detail exit 1 end exit 0 end command(:main) do # Set our code or file to use. if options[:code] or ARGV.length == 0 Puppet[:code] = options[:code] || STDIN.read else Puppet[:manifest] = ARGV.shift end # Collect our facts. unless facts = Puppet::Node::Facts.find(Puppet[:certname]) raise "Could not find facts for %s" % Puppet[:certname] end # Find our Node unless node = Puppet::Node.find(Puppet[:certname]) raise "Could not find node %s" % Puppet[:certname] end # Merge in the facts. node.merge(facts.values) # Allow users to load the classes that puppetd creates. if options[:loadclasses] file = Puppet[:classfile] if FileTest.exists?(file) unless FileTest.readable?(file) $stderr.puts "%s is not readable" % file exit(63) end node.classes = File.read(file).split(/[\s\n]+/) end end begin # Compile our catalog starttime = Time.now catalog = Puppet::Resource::Catalog.find(node.name, :use_node => node) # Translate it to a RAL catalog catalog = catalog.to_ral catalog.host_config = true if Puppet[:graph] or Puppet[:report] catalog.finalize catalog.retrieval_duration = Time.now - starttime configurer = Puppet::Configurer.new configurer.execute_prerun_command # And apply it transaction = catalog.apply configurer.execute_postrun_command status = 0 if not Puppet[:noop] and options[:detailed_exitcodes] then transaction.generate_report exit(transaction.report.exit_status) else exit(0) end rescue => detail puts detail.backtrace if Puppet[:trace] if detail.is_a?(XMLRPC::FaultException) $stderr.puts detail.message else $stderr.puts detail end exit(1) end end setup do if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end # If noop is set, then also enable diffs if Puppet[:noop] Puppet[:show_diff] = true end unless options[:logset] Puppet::Util::Log.newdestination(:console) end client = nil server = nil trap(:INT) do $stderr.puts "Exiting" exit(1) end if options[:debug] Puppet::Util::Log.level = :debug elsif options[:verbose] Puppet::Util::Log.level = :info end end end diff --git a/lib/puppet/application/server.rb b/lib/puppet/application/server.rb index 57d29e0ee..e9253c6f9 100644 --- a/lib/puppet/application/server.rb +++ b/lib/puppet/application/server.rb @@ -1,168 +1,168 @@ require 'puppet' require 'puppet/application' require 'puppet/daemon' require 'puppet/network/server' require 'puppet/network/http/rack' if Puppet.features.rack? Puppet::Application.new(:server) do should_parse_config option("--debug", "-d") option("--verbose", "-v") # internal option, only to be used by ext/rack/config.ru option("--rack") option("--compile host", "-c host") do |arg| options[:node] = arg end option("--logdest DEST", "-l DEST") do |arg| begin Puppet::Util::Log.newdestination(arg) options[:setdest] = true rescue => detail if Puppet[:debug] puts detail.backtrace end $stderr.puts detail.to_s end end preinit do trap(:INT) do $stderr.puts "Cancelling startup" exit(0) end # Create this first-off, so we have ARGV @daemon = Puppet::Daemon.new @daemon.argv = ARGV.dup end dispatch do if options[:node] :compile elsif Puppet[:parseonly] :parseonly else :main end end command(:compile) do Puppet::Util::Log.newdestination :console raise ArgumentError, "Cannot render compiled catalogs without pson support" unless Puppet.features.pson? begin unless catalog = Puppet::Resource::Catalog.find(options[:node]) raise "Could not compile catalog for %s" % options[:node] end $stdout.puts catalog.render(:pson) rescue => detail $stderr.puts detail exit(30) end exit(0) end command(:parseonly) do begin - Puppet::Parser::Interpreter.new.parser(Puppet[:environment]) + Puppet::Parser::ResourceTypeCollection.new(Puppet[:environment]).perform_initial_import rescue => detail Puppet.err detail exit 1 end exit(0) end command(:main) do require 'etc' require 'puppet/file_serving/content' require 'puppet/file_serving/metadata' require 'puppet/checksum' xmlrpc_handlers = [:Status, :FileServer, :Master, :Report, :Filebucket] if Puppet[:ca] xmlrpc_handlers << :CA end # Make sure we've got a localhost ssl cert Puppet::SSL::Host.localhost # And now configure our server to *only* hit the CA for data, because that's # all it will have write access to. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :only end if Process.uid == 0 begin Puppet::Util.chuser rescue => detail puts detail.backtrace if Puppet[:trace] $stderr.puts "Could not change user to %s: %s" % [Puppet[:user], detail] exit(39) end end unless options[:rack] @daemon.server = Puppet::Network::Server.new(:xmlrpc_handlers => xmlrpc_handlers) @daemon.daemonize if Puppet[:daemonize] else require 'puppet/network/http/rack' @app = Puppet::Network::HTTP::Rack.new(:xmlrpc_handlers => xmlrpc_handlers, :protocols => [:rest, :xmlrpc]) end Puppet.notice "Starting Puppet server version %s" % [Puppet.version] unless options[:rack] @daemon.start else return @app end end setup do # Handle the logging settings. if options[:debug] or options[:verbose] if options[:debug] Puppet::Util::Log.level = :debug else Puppet::Util::Log.level = :info end unless Puppet[:daemonize] or options[:rack] Puppet::Util::Log.newdestination(:console) options[:setdest] = true end end unless options[:setdest] Puppet::Util::Log.newdestination(:syslog) end if Puppet.settings.print_configs? exit(Puppet.settings.print_configs ? 0 : 1) end Puppet.settings.use :main, :puppetmasterd, :ssl # A temporary solution, to at least make the master work for now. Puppet::Node::Facts.terminus_class = :yaml # Cache our nodes in yaml. Currently not configurable. Puppet::Node.cache_class = :yaml # Configure all of the SSL stuff. if Puppet::SSL::CertificateAuthority.ca? Puppet::SSL::Host.ca_location = :local Puppet.settings.use :ca Puppet::SSL::CertificateAuthority.instance else Puppet::SSL::Host.ca_location = :none end end end diff --git a/lib/puppet/indirector/catalog/compiler.rb b/lib/puppet/indirector/catalog/compiler.rb index ecb1c74e1..f83245a27 100644 --- a/lib/puppet/indirector/catalog/compiler.rb +++ b/lib/puppet/indirector/catalog/compiler.rb @@ -1,183 +1,168 @@ require 'puppet/node' require 'puppet/resource/catalog' require 'puppet/indirector/code' -require 'puppet/parser/interpreter' require 'yaml' class Puppet::Resource::Catalog::Compiler < Puppet::Indirector::Code desc "Puppet's catalog compilation interface, and its back-end is Puppet's compiler" include Puppet::Util attr_accessor :code def extract_facts_from_request(request) return unless text_facts = request.options[:facts] raise ArgumentError, "Facts but no fact format provided for %s" % request.name unless format = request.options[:facts_format] # If the facts were encoded as yaml, then the param reconstitution system # in Network::HTTP::Handler will automagically deserialize the value. if text_facts.is_a?(Puppet::Node::Facts) facts = text_facts else facts = Puppet::Node::Facts.convert_from(format, text_facts) end facts.save end # Compile a node's catalog. def find(request) extract_facts_from_request(request) node = node_from_request(request) if catalog = compile(node) return catalog else # This shouldn't actually happen; we should either return # a config or raise an exception. return nil end end # filter-out a catalog to remove exported resources def filter(catalog) return catalog.filter { |r| r.virtual? } if catalog.respond_to?(:filter) catalog end def initialize set_server_facts setup_database_backend if Puppet[:storeconfigs] end - # Create/return our interpreter. - def interpreter - unless defined?(@interpreter) and @interpreter - @interpreter = create_interpreter - end - @interpreter - end - # Is our compiler part of a network, or are we just local? def networked? $0 =~ /puppetmasterd/ end private # Add any extra data necessary to the node. def add_node_data(node) # Merge in our server-side facts, so they can be used during compilation. node.merge(@server_facts) end # Compile the actual catalog. def compile(node) - # Ask the interpreter to compile the catalog. str = "Compiled catalog for %s" % node.name if node.environment str += " in environment %s" % node.environment end config = nil loglevel = networked? ? :notice : :none benchmark(loglevel, "Compiled catalog for %s" % node.name) do begin - config = interpreter.compile(node) + return Puppet::Parser::Compiler.compile(node) rescue Puppet::Error => detail Puppet.err(detail.to_s) if networked? raise end end return config end - # Create our interpreter object. - def create_interpreter - return Puppet::Parser::Interpreter.new - end - # Turn our host name into a node object. def find_node(name) begin return nil unless node = Puppet::Node.find(name) rescue => detail puts detail.backtrace if Puppet[:trace] raise Puppet::Error, "Failed when searching for node %s: %s" % [name, detail] end # Add any external data to the node. add_node_data(node) node end # Extract the node from the request, or use the request # to find the node. def node_from_request(request) if node = request.options[:use_node] return node end # If the request is authenticated, then the 'node' info will # be available; if not, then we use the passed-in key. We rely # on our authorization system to determine whether this is allowed. name = request.node || request.key if node = find_node(name) return node end raise ArgumentError, "Could not find node '%s'; cannot compile" % name end # Initialize our server fact hash; we add these to each client, and they # won't change while we're running, so it's safe to cache the values. def set_server_facts @server_facts = {} # Add our server version to the fact list @server_facts["serverversion"] = Puppet.version.to_s # And then add the server name and IP {"servername" => "fqdn", "serverip" => "ipaddress" }.each do |var, fact| if value = Facter.value(fact) @server_facts[var] = value else Puppet.warning "Could not retrieve fact %s" % fact end end if @server_facts["servername"].nil? host = Facter.value(:hostname) if domain = Facter.value(:domain) @server_facts["servername"] = [host, domain].join(".") else @server_facts["servername"] = host end end end def setup_database_backend raise Puppet::Error, "Rails is missing; cannot store configurations" unless Puppet.features.rails? Puppet::Rails.init end # Mark that the node has checked in. LAK:FIXME this needs to be moved into # the Node class, or somewhere that's got abstract backends. def update_node_check(node) if Puppet.features.rails? and Puppet[:storeconfigs] Puppet::Rails.connect host = Puppet::Rails::Host.find_or_create_by_name(node.name) host.last_freshcheck = Time.now host.save end end end diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb index e21e1e6e3..d123fd6fc 100644 --- a/lib/puppet/network/handler/master.rb +++ b/lib/puppet/network/handler/master.rb @@ -1,102 +1,99 @@ require 'openssl' require 'puppet' -require 'puppet/parser/interpreter' require 'puppet/sslcertificates' require 'xmlrpc/server' require 'yaml' class Puppet::Network::Handler class MasterError < Puppet::Error; end class Master < Handler desc "Puppet's configuration interface. Used for all interactions related to generating client configurations." include Puppet::Util attr_accessor :ast attr_reader :ca @interface = XMLRPC::Service::Interface.new("puppetmaster") { |iface| iface.add_method("string getconfig(string)") iface.add_method("int freshness()") } # Tell a client whether there's a fresh config for it def freshness(client = nil, clientip = nil) # Always force a recompile. Newer clients shouldn't do this (as of April 2008). return Time.now.to_i end def initialize(hash = {}) args = {} if hash[:Local] @local = hash[:Local] else @local = false end args[:Local] = true if hash.include?(:CA) and hash[:CA] @ca = Puppet::SSLCertificates::CA.new() else @ca = nil end - Puppet.debug("Creating interpreter") - # This is only used by the cfengine module, or if --loadclasses was # specified in +puppet+. if hash.include?(:Classes) args[:Classes] = hash[:Classes] end end # Call our various handlers; this handler is getting deprecated. def getconfig(facts, format = "marshal", client = nil, clientip = nil) facts = decode_facts(facts) client ||= facts["hostname"] # Pass the facts to the fact handler Puppet::Node::Facts.new(client, facts).save unless local? catalog = Puppet::Resource::Catalog.find(client) case format when "yaml" return CGI.escape(catalog.extract.to_yaml(:UseBlock => true)) when "marshal" return CGI.escape(Marshal.dump(catalog.extract)) else raise "Invalid markup format '%s'" % format end end # def decode_facts(facts) if @local # we don't need to do anything, since we should already # have raw objects Puppet.debug "Our client is local" else Puppet.debug "Our client is remote" begin facts = YAML.load(CGI.unescape(facts)) rescue => detail raise XMLRPC::FaultException.new( 1, "Could not rebuild facts" ) end end return facts end # Translate our configuration appropriately for sending back to a client. def translate(config) end end end diff --git a/lib/puppet/parser.rb b/lib/puppet/parser.rb new file mode 100644 index 000000000..3eda73885 --- /dev/null +++ b/lib/puppet/parser.rb @@ -0,0 +1,4 @@ +require 'puppet/parser/parser' +require 'puppet/parser/compiler' +require 'puppet/parser/resource_type_collection' + diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb index f9c8f70ae..25b064195 100644 --- a/lib/puppet/parser/compiler.rb +++ b/lib/puppet/parser/compiler.rb @@ -1,403 +1,410 @@ # Created by Luke A. Kanies on 2007-08-13. # Copyright (c) 2007. All rights reserved. require 'puppet/node' require 'puppet/resource/catalog' require 'puppet/util/errors' require 'puppet/parser/resource_type_collection_helper' # Maintain a graph of scopes, along with a bunch of data # about the individual catalog we're compiling. class Puppet::Parser::Compiler include Puppet::Util include Puppet::Util::Errors include Puppet::Parser::ResourceTypeCollectionHelper + def self.compile(node) + new(node).compile.to_resource + rescue => detail + puts detail.backtrace if Puppet[:trace] + raise Puppet::Error, "#{detail} on node #{node.name}" + end + attr_reader :node, :facts, :collections, :catalog, :node_scope, :resources # Add a collection to the global list. def add_collection(coll) @collections << coll end # Store a resource override. def add_override(override) # If possible, merge the override in immediately. if resource = @catalog.resource(override.ref) resource.merge(override) else # Otherwise, store the override for later; these # get evaluated in Resource#finish. @resource_overrides[override.ref] << override end end # Store a resource in our resource table. def add_resource(scope, resource) @resources << resource # Note that this will fail if the resource is not unique. @catalog.add_resource(resource) # And in the resource graph. At some point, this might supercede # the global resource table, but the table is a lot faster # so it makes sense to maintain for now. if resource.type.to_s.downcase == "class" and main = @catalog.resource(:class, :main) @catalog.add_edge(main, resource) else @catalog.add_edge(scope.resource, resource) end end # Do we use nodes found in the code, vs. the external node sources? def ast_nodes? known_resource_types.nodes? end # Store the fact that we've evaluated a class def add_class(name) @catalog.add_class(name) unless name == "" end # Return a list of all of the defined classes. def classlist return @catalog.classes end # Compiler our catalog. This mostly revolves around finding and evaluating classes. # This is the main entry into our catalog. def compile # Set the client's parameters into the top scope. set_node_parameters() evaluate_main() evaluate_ast_node() evaluate_node_classes() evaluate_generators() finish() fail_on_unevaluated() return @catalog end # LAK:FIXME There are no tests for this. def delete_collection(coll) @collections.delete(coll) if @collections.include?(coll) end # Return the node's environment. def environment unless defined? @environment if node.environment and node.environment != "" @environment = node.environment else @environment = nil end end @environment end # Evaluate all of the classes specified by the node. def evaluate_node_classes evaluate_classes(@node.classes, topscope) end # Evaluate each specified class in turn. If there are any classes we can't # find, just tag the catalog and move on. This method really just # creates resource objects that point back to the classes, and then the # resources are themselves evaluated later in the process. def evaluate_classes(classes, scope, lazy_evaluate = true) unless scope.source raise Puppet::DevError, "No source for scope passed to evaluate_classes" end found = [] classes.each do |name| # If we can find the class, then make a resource that will evaluate it. if klass = scope.find_hostclass(name) found << name and next if scope.class_scope(klass) resource = klass.mk_plain_resource(scope) # If they've disabled lazy evaluation (which the :include function does), # then evaluate our resource immediately. resource.evaluate unless lazy_evaluate found << name else Puppet.info "Could not find class %s for %s" % [name, node.name] @catalog.tag(name) end end found end # Return a resource by either its ref or its type and title. def findresource(*args) @catalog.resource(*args) end def initialize(node, options = {}) @node = node options.each do |param, value| begin send(param.to_s + "=", value) rescue NoMethodError raise ArgumentError, "Compiler objects do not accept %s" % param end end initvars() init_main() end # Create a new scope, with either a specified parent scope or # using the top scope. def newscope(parent, options = {}) parent ||= topscope options[:compiler] = self scope = Puppet::Parser::Scope.new(options) scope.parent = parent scope end # Return any overrides for the given resource. def resource_overrides(resource) @resource_overrides[resource.ref] end # The top scope is usually the top-level scope, but if we're using AST nodes, # then it is instead the node's scope. def topscope node_scope || @topscope end private # If ast nodes are enabled, then see if we can find and evaluate one. def evaluate_ast_node return unless ast_nodes? # Now see if we can find the node. astnode = nil @node.names.each do |name| break if astnode = known_resource_types.node(name.to_s.downcase) end unless (astnode ||= known_resource_types.node("default")) raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ") end # Create a resource to model this node, and then add it to the list # of resources. resource = astnode.mk_plain_resource(topscope) resource.evaluate # Now set the node scope appropriately, so that :topscope can # behave differently. @node_scope = topscope.class_scope(astnode) end # Evaluate our collections and return true if anything returned an object. # The 'true' is used to continue a loop, so it's important. def evaluate_collections return false if @collections.empty? found_something = false exceptwrap do # We have to iterate over a dup of the array because # collections can delete themselves from the list, which # changes its length and causes some collections to get missed. @collections.dup.each do |collection| found_something = true if collection.evaluate end end return found_something end # Make sure all of our resources have been evaluated into native resources. # We return true if any resources have, so that we know to continue the # evaluate_generators loop. def evaluate_definitions exceptwrap do if ary = unevaluated_resources evaluated = false ary.each do |resource| if not resource.virtual? resource.evaluate evaluated = true end end # If we evaluated, let the loop know. return evaluated else return false end end end # Iterate over collections and resources until we're sure that the whole # compile is evaluated. This is necessary because both collections # and defined resources can generate new resources, which themselves could # be defined resources. def evaluate_generators count = 0 loop do done = true # Call collections first, then definitions. done = false if evaluate_collections done = false if evaluate_definitions break if done count += 1 if count > 1000 raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host catalog" end end end # Find and evaluate our main object, if possible. def evaluate_main @main = known_resource_types.find_hostclass("", "") || known_resource_types.add(Puppet::Parser::ResourceType.new(:hostclass, "")) @topscope.source = @main @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) @topscope.resource = @main_resource @resources << @main_resource @catalog.add_resource(@main_resource) @main_resource.evaluate end # Make sure the entire catalog is evaluated. def fail_on_unevaluated fail_on_unevaluated_overrides fail_on_unevaluated_resource_collections end # If there are any resource overrides remaining, then we could # not find the resource they were supposed to override, so we # want to throw an exception. def fail_on_unevaluated_overrides remaining = [] @resource_overrides.each do |name, overrides| remaining += overrides end unless remaining.empty? fail Puppet::ParseError, "Could not find resource(s) %s for overriding" % remaining.collect { |o| o.ref }.join(", ") end end # Make sure we don't have any remaining collections that specifically # look for resources, because we want to consider those to be # parse errors. def fail_on_unevaluated_resource_collections remaining = [] @collections.each do |coll| # We're only interested in the 'resource' collections, # which result from direct calls of 'realize'. Anything # else is allowed not to return resources. # Collect all of them, so we have a useful error. if r = coll.resources if r.is_a?(Array) remaining += r else remaining << r end end end unless remaining.empty? raise Puppet::ParseError, "Failed to realize virtual resources %s" % remaining.join(', ') end end # Make sure all of our resources and such have done any last work # necessary. def finish resources.each do |resource| # Add in any resource overrides. if overrides = resource_overrides(resource) overrides.each do |over| resource.merge(over) end # Remove the overrides, so that the configuration knows there # are none left. overrides.clear end resource.finish if resource.respond_to?(:finish) end end # Initialize the top-level scope, class, and resource. def init_main # Create our initial scope and a resource that will evaluate main. @topscope = Puppet::Parser::Scope.new(:compiler => self) end # Set up all of our internal variables. def initvars # The list of objects that will available for export. @exported_resources = {} # The list of overrides. This is used to cache overrides on objects # that don't exist yet. We store an array of each override. @resource_overrides = Hash.new do |overs, ref| overs[ref] = [] end # The list of collections that have been created. This is a global list, # but they each refer back to the scope that created them. @collections = [] # For maintaining the relationship between scopes and their resources. @catalog = Puppet::Resource::Catalog.new(@node.name) @catalog.version = known_resource_types.version # local resource array to maintain resource ordering @resources = [] # Make sure any external node classes are in our class list @catalog.add_class(*@node.classes) end # Set the node's parameters into the top-scope as variables. def set_node_parameters node.parameters.each do |param, value| @topscope.setvar(param, value) end # These might be nil. catalog.client_version = node.parameters["clientversion"] catalog.server_version = node.parameters["serverversion"] end # Return an array of all of the unevaluated resources. These will be definitions, # which need to get evaluated into native resources. def unevaluated_resources ary = resources.reject { |resource| resource.builtin? or resource.evaluated? } if ary.empty? return nil else return ary end end end diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra index 76a82e38e..0e090187d 100644 --- a/lib/puppet/parser/grammar.ra +++ b/lib/puppet/parser/grammar.ra @@ -1,863 +1,862 @@ # vim: syntax=ruby # the parser class Puppet::Parser::Parser token LBRACK DQTEXT SQTEXT RBRACK LBRACE RBRACE SYMBOL FARROW COMMA TRUE token FALSE EQUALS APPENDS LESSEQUAL NOTEQUAL DOT COLON LLCOLLECT RRCOLLECT 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 CLASSNAME CLASSREF token NOT OR AND UNDEF PARROW PLUS MINUS TIMES DIV LSHIFT RSHIFT UMINUS token MATCH NOMATCH REGEX prechigh right NOT nonassoc UMINUS left MATCH NOMATCH left TIMES DIV left MINUS PLUS left LSHIFT RSHIFT left NOTEQUAL ISEQUAL left GREATEREQUAL GREATERTHAN LESSTHAN LESSEQUAL left AND left OR preclow rule program: statements { if val[0] # Make sure we always return an array. if val[0].is_a?(AST::ASTArray) if val[0].children.empty? result = nil else result = val[0] end else result = aryfy(val[0]) end else result = nil end } | nil statements: statement | statements statement { if val[0] and val[1] 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 elsif obj = (val[0] || val[1]) result = obj else result = nil end } # The main list of valid statements statement: resource | virtualresource | collection | assignment | casestatement | ifstatement | import | fstatement | definition | hostclass | nodedef | resourceoverride | append fstatement: NAME LPAREN funcvalues RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :statement } | NAME LPAREN funcvalues COMMA RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :statement } | NAME LPAREN RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :statement } | NAME funcvalues { args = aryfy(val[1]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :statement } funcvalues: namestring | resourceref | funcvalues COMMA namestring { result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file } | funcvalues COMMA resourceref { unless val[0].is_a?(AST::ASTArray) val[0] = aryfy(val[0]) end val[0].push(val[2]) result = val[0] } # This is *almost* an rvalue, but I couldn't get a full # rvalue to work without scads of shift/reduce conflicts. namestring: name | variable | type | boolean | funcrvalue | selector | quotedtext | hasharrayaccesses | CLASSNAME { result = ast AST::Name, :value => val[0][:value] } resource: classname LBRACE resourceinstances endsemi RBRACE { @lexer.commentpop array = val[2] if array.instance_of?(AST::ResourceInstance) array = [array] end result = ast AST::ASTArray # this iterates across each specified resourceinstance array.each { |instance| unless instance.instance_of?(AST::ResourceInstance) 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::Resource, :type => val[0], :title => instance[0], :params => instance[1]) } } | classname LBRACE params endcomma RBRACE { # This is a deprecated syntax. error "All resource specifications require names" } | classref LBRACE params endcomma RBRACE { # a defaults setting for a type result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2]) } # Override a value set elsewhere in the configuration. resourceoverride: resourceref LBRACE anyparams endcomma RBRACE { @lexer.commentpop result = ast AST::ResourceOverride, :object => val[0], :params => val[2] } # Exported and virtual resources; these don't get sent to the client # unless they get collected elsewhere in the db. virtualresource: at resource { type = val[0] if (type == :exported and ! Puppet[:storeconfigs]) and ! Puppet[:parseonly] Puppet.warning addcontext("You cannot collect without storeconfigs being set") end if val[1].is_a? AST::ResourceDefaults error "Defaults are not virtualizable" end method = type.to_s + "=" # Just mark our resources as exported and pass them through. if val[1].instance_of?(AST::ASTArray) val[1].each do |obj| obj.send(method, true) end else val[1].send(method, true) end result = val[1] } at: AT { result = :virtual } | AT AT { result = :exported } # A collection statement. Currently supports no arguments at all, but eventually # will, I assume. collection: classref collectrhand LBRACE anyparams endcomma RBRACE { if val[0] =~ /^[a-z]/ Puppet.warning addcontext("Collection names must now be capitalized") end type = val[0].downcase args = {:type => type} if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end if args[:form] == :exported and ! Puppet[:storeconfigs] and ! Puppet[:parseonly] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end args[:override] = val[3] result = ast AST::Collection, args } | classref collectrhand { if val[0] =~ /^[a-z]/ Puppet.warning addcontext("Collection names must now be capitalized") end type = val[0].downcase args = {:type => type } if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end if args[:form] == :exported and ! Puppet[:storeconfigs] and ! Puppet[:parseonly] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end result = ast AST::Collection, args } collectrhand: LCOLLECT collstatements RCOLLECT { if val[1] result = val[1] result.form = :virtual else result = :virtual end } | LLCOLLECT collstatements RRCOLLECT { if val[1] result = val[1] result.form = :exported else result = :exported end } # A mini-language for handling collection comparisons. This is organized # to avoid the need for precedence indications. collstatements: nil | collstatement | collstatements colljoin collstatement { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] } collstatement: collexpr | LPAREN collstatements RPAREN { result = val[1] result.parens = true } colljoin: AND { result=val[0][:value] } | OR { result=val[0][:value] } collexpr: colllval ISEQUAL simplervalue { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val } | colllval NOTEQUAL simplervalue { result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val } colllval: variable | name resourceinst: resourcename COLON params endcomma { result = ast AST::ResourceInstance, :children => [val[0],val[2]] } resourceinstances: resourceinst | resourceinstances SEMIC resourceinst { if val[0].instance_of?(AST::ResourceInstance) result = ast AST::ASTArray, :children => [val[0],val[2]] else val[0].push val[2] result = val[0] end } endsemi: # nothing | SEMIC undef: UNDEF { result = ast AST::Undef, :value => :undef } name: NAME { result = ast AST::Name, :value => val[0][:value], :line => val[0][:line] } type: CLASSREF { result = ast AST::Type, :value => val[0][:value], :line => val[0][:line] } resourcename: quotedtext | name | type | selector | variable | array | hasharrayaccesses assignment: VARIABLE EQUALS expression { if val[0][:value] =~ /::/ raise Puppet::ParseError, "Cannot assign to variables in other namespaces" end # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :line => val[0][:line] } | hasharrayaccess EQUALS expression { result = ast AST::VarDef, :name => val[0], :value => val[2] } append: VARIABLE APPENDS expression { variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :append => true, :line => val[0][:line] } 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 { result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2] } addparam: NAME PARROW rvalue { result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2], :add => true } anyparam: param | addparam anyparams: # nothing { result = ast AST::ASTArray } | anyparam { result = val[0] } | anyparams COMMA anyparam { 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 } 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 } simplervalue: quotedtext | name | type | boolean | selector | variable rvalue: quotedtext | name | type | boolean | selector | variable | array | hash | hasharrayaccesses | resourceref | funcrvalue | undef # We currently require arguments in these functions. funcrvalue: NAME LPAREN funcvalues RPAREN { args = aryfy(val[2]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :rvalue } | NAME LPAREN RPAREN { result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :rvalue } quotedtext: DQTEXT { result = ast AST::String, :value => val[0][:value], :line => val[0][:line] } | SQTEXT { result = ast AST::FlatString, :value => val[0][:value], :line => val[0][:line] } boolean: BOOLEAN { result = ast AST::Boolean, :value => val[0][:value], :line => val[0][:line] } resourceref: NAME LBRACK rvalues RBRACK { Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0][:value], :line => val[0][:line], :title => val[2] } | classref LBRACK rvalues RBRACK { result = ast AST::ResourceReference, :type => val[0], :title => val[2] } ifstatement: IF expression LBRACE statements RBRACE else { @lexer.commentpop args = { :test => val[1], :statements => val[3] } if val[5] args[:else] = val[5] end result = ast AST::IfStatement, args } | IF expression LBRACE RBRACE else { @lexer.commentpop args = { :test => val[1], :statements => ast(AST::Nop) } if val[4] args[:else] = val[4] end result = ast AST::IfStatement, args } else: # nothing | ELSE LBRACE statements RBRACE { @lexer.commentpop result = ast AST::Else, :statements => val[2] } | ELSE LBRACE RBRACE { @lexer.commentpop result = ast AST::Else, :statements => ast(AST::Nop) } # Unlike yacc/bison, it seems racc # gives tons of shift/reduce warnings # with the following syntax: # # expression: ... # | expression arithop expressio { ... } # # arithop: PLUS | MINUS | DIVIDE | TIMES ... # # So I had to develop the expression by adding one rule # per operator :-( expression: rvalue | expression MATCH regex { result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression NOMATCH regex { result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression PLUS expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression MINUS expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression DIV expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression TIMES expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression LSHIFT expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression RSHIFT expression { result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | MINUS expression =UMINUS { result = ast AST::Minus, :value => val[1] } | expression NOTEQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression ISEQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression GREATERTHAN expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression GREATEREQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression LESSTHAN expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression LESSEQUAL expression { result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | NOT expression { result = ast AST::Not, :value => val[1] } | expression AND expression { result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | expression OR expression { result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] } | LPAREN expression RPAREN { result = val[1] } casestatement: CASE rvalue LBRACE caseopts RBRACE { @lexer.commentpop 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 { @lexer.commentpop result = ast AST::CaseOpt, :value => val[0], :statements => val[3] } | casevalues COLON LBRACE RBRACE { @lexer.commentpop 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 endcomma RBRACE { @lexer.commentpop 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::ResourceParam, :param => val[0], :value => val[2] } selectlhand: name | type | quotedtext | variable | funcrvalue | boolean | undef | DEFAULT { result = ast AST::Default, :value => val[0][:value], :line => val[0][:line] } | regex # These are only used for importing, and we don't interpolate there. qtexts: quotedtext { result = [val[0].value] } | qtexts COMMA quotedtext { results = val[0] << val[2].value } import: IMPORT qtexts { val[1].each do |file| import(file) end result = AST::ASTArray.new(:children => []) } # Disable definition inheritance for now. 8/27/06, luke #definition: DEFINE NAME argumentlist parent LBRACE statements RBRACE { definition: DEFINE classname argumentlist LBRACE statements RBRACE { @lexer.commentpop newdefine classname(val[1]), :arguments => val[2], :code => val[4], :line => val[0][:line] @lexer.indefine = false result = nil #} | DEFINE NAME argumentlist parent LBRACE RBRACE { } | DEFINE classname argumentlist LBRACE RBRACE { @lexer.commentpop newdefine classname(val[1]), :arguments => val[2], :line => val[0][:line] @lexer.indefine = false result = nil } #hostclass: CLASS NAME argumentlist parent LBRACE statements RBRACE { hostclass: CLASS classname classparent LBRACE statements RBRACE { @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :code => val[4], :parent => val[2], :line => val[0][:line] result = nil } | CLASS classname classparent LBRACE RBRACE { @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :parent => val[2], :line => val[0][:line] result = nil } nodedef: NODE hostnames nodeparent LBRACE statements RBRACE { @lexer.commentpop newnode val[1], :parent => val[2], :code => val[4], :line => val[0][:line] result = nil } | NODE hostnames nodeparent LBRACE RBRACE { @lexer.commentpop newnode val[1], :parent => val[2], :line => val[0][:line] result = nil } classref: CLASSREF { result = val[0][:value] } classname: NAME { result = val[0][:value] } | CLASSNAME { result = val[0][:value] } # Multiple hostnames, as used for node names. These are all literal # strings, not AST objects. hostnames: nodename | hostnames COMMA nodename { result = val[0] result = [result] unless result.is_a?(Array) result << val[2] } nodename: hostname { result = ast AST::HostName, :value => val[0] } hostname: NAME { result = val[0][:value] } | SQTEXT { result = val[0][:value] } | DQTEXT { result = val[0][:value] } | DEFAULT { result = val[0][:value] } | regex nil: { result = nil } nothing: { result = ast AST::ASTArray, :children => [] } argumentlist: nil | LPAREN nothing RPAREN { result = nil } | LPAREN arguments RPAREN { result = val[1] result = [result] unless result[0].is_a?(Array) } arguments: argument | arguments COMMA argument { result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] } argument: NAME EQUALS rvalue { Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value], val[2]] } | NAME { Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value]] } | VARIABLE EQUALS rvalue { result = [val[0][:value], val[2]] } | VARIABLE { result = [val[0][:value]] } nodeparent: nil | INHERITS hostname { result = val[1] } classparent: nil | INHERITS classnameordefault { result = val[1] } classnameordefault: classname | DEFAULT variable: VARIABLE { result = ast AST::Variable, :value => val[0][:value], :line => val[0][:line] } array: LBRACK rvalues RBRACK { if val[1].instance_of?(AST::ASTArray) result = val[1] else result = ast AST::ASTArray, :children => [val[1]] end } | LBRACK rvalues COMMA 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 } regex: REGEX { result = ast AST::Regex, :value => val[0][:value] } hash: LBRACE hashpairs RBRACE { if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end } | LBRACE hashpairs COMMA RBRACE { if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end } | LBRACE RBRACE { result = ast AST::ASTHash } hashpairs: hashpair | hashpairs COMMA hashpair { if val[0].instance_of?(AST::ASTHash) result = val[0].merge(val[2]) else result = ast AST::ASTHash, :value => val[0] result.merge(val[2]) end } hashpair: key FARROW rvalue { result = ast AST::ASTHash, { :value => { val[0] => val[2] } } } key: NAME { result = val[0][:value] } | SQTEXT { result = val[0][:value] } | DQTEXT { result = val[0][:value] } hasharrayaccess: VARIABLE LBRACK rvalue RBRACK { result = ast AST::HashOrArrayAccess, :variable => val[0][:value], :key => val[2] } hasharrayaccesses: hasharrayaccess | hasharrayaccess LBRACK rvalue RBRACK { result = ast AST::HashOrArrayAccess, :variable => val[0], :key => val[2] } end ---- header ---- require 'puppet' require 'puppet/util/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 class AlreadyImportedError < ImportError; end end Puppet[:typecheck] = true Puppet[:paramcheck] = true ---- inner ---- # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' # Make emacs happy # Local Variables: # mode: ruby # End: # $Id$ diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb deleted file mode 100644 index eea9afcad..000000000 --- a/lib/puppet/parser/interpreter.rb +++ /dev/null @@ -1,69 +0,0 @@ -require 'puppet' -require 'timeout' -require 'puppet/rails' -require 'puppet/util/methodhelper' -require 'puppet/parser/parser' -require 'puppet/parser/compiler' -require 'puppet/parser/scope' - -# The interpreter is a very simple entry-point class that -# manages the existence of the parser (e.g., replacing it -# when files are reparsed). You can feed it a node and -# get the node's catalog back. -class Puppet::Parser::Interpreter - include Puppet::Util - - attr_accessor :usenodes - - include Puppet::Util::Errors - - # evaluate our whole tree - def compile(node) - begin - return Puppet::Parser::Compiler.new(node).compile.to_resource - rescue => detail - puts detail.backtrace if Puppet[:trace] - raise Puppet::Error, detail.to_s + " on node %s" % node.name - end - end - - # create our interpreter - def initialize - @parsers = {} - end - - # Return the parser for a specific environment. - def parser(environment) - if ! @parsers[environment] or @parsers[environment].reparse? - # This will throw an exception if it does not succeed. - @parsers[environment] = create_parser(environment) - end - @parsers[environment] - end - - private - - # Create a new parser object and pre-parse the configuration. - def create_parser(environment) - begin - parser = Puppet::Parser::Parser.new(environment) - if code = Puppet.settings.uninterpolated_value(:code, environment) and code != "" - parser.string = code - else - file = Puppet.settings.value(:manifest, environment) - parser.file = file - end - parser.parse - return parser - rescue => detail - msg = "Could not parse" - if environment and environment != "" - msg += " for environment %s" % environment - end - msg += ": %s" % detail.to_s - error = Puppet::Error.new(msg) - error.set_backtrace(detail.backtrace) - raise error - end - end -end diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb index 7e9235f66..a3dc5be9b 100644 --- a/lib/puppet/parser/parser.rb +++ b/lib/puppet/parser/parser.rb @@ -1,2461 +1,2460 @@ # # 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/util/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 class AlreadyImportedError < ImportError; end end Puppet[:typecheck] = true Puppet[:paramcheck] = true module Puppet module Parser class Parser < Racc::Parser -module_eval <<'..end grammar.ra modeval..id884310a196', 'grammar.ra', 853 +module_eval <<'..end grammar.ra modeval..id8da59540c6', 'grammar.ra', 852 # It got too annoying having code in a file that needs to be compiled. require 'puppet/parser/parser_support' # Make emacs happy # Local Variables: # mode: ruby # End: # $Id$ -..end grammar.ra modeval..id884310a196 +..end grammar.ra modeval..id8da59540c6 ##### racc 1.4.5 generates ### racc_reduce_table = [ 0, 0, :racc_error, 1, 63, :_reduce_1, 1, 63, :_reduce_none, 1, 64, :_reduce_none, 2, 64, :_reduce_4, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 1, 66, :_reduce_none, 4, 74, :_reduce_18, 5, 74, :_reduce_19, 3, 74, :_reduce_20, 2, 74, :_reduce_21, 1, 80, :_reduce_none, 1, 80, :_reduce_none, 3, 80, :_reduce_24, 3, 80, :_reduce_25, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_none, 1, 81, :_reduce_34, 5, 67, :_reduce_35, 5, 67, :_reduce_36, 5, 67, :_reduce_37, 5, 78, :_reduce_38, 2, 68, :_reduce_39, 1, 98, :_reduce_40, 2, 98, :_reduce_41, 6, 69, :_reduce_42, 2, 69, :_reduce_43, 3, 99, :_reduce_44, 3, 99, :_reduce_45, 1, 100, :_reduce_none, 1, 100, :_reduce_none, 3, 100, :_reduce_48, 1, 101, :_reduce_none, 3, 101, :_reduce_50, 1, 102, :_reduce_51, 1, 102, :_reduce_52, 3, 103, :_reduce_53, 3, 103, :_reduce_54, 1, 104, :_reduce_none, 1, 104, :_reduce_none, 4, 106, :_reduce_57, 1, 92, :_reduce_none, 3, 92, :_reduce_59, 0, 93, :_reduce_none, 1, 93, :_reduce_none, 1, 108, :_reduce_62, 1, 83, :_reduce_63, 1, 85, :_reduce_64, 1, 107, :_reduce_none, 1, 107, :_reduce_none, 1, 107, :_reduce_none, 1, 107, :_reduce_none, 1, 107, :_reduce_none, 1, 107, :_reduce_none, 1, 107, :_reduce_none, 3, 70, :_reduce_72, 3, 70, :_reduce_73, 3, 79, :_reduce_74, 0, 94, :_reduce_75, 1, 94, :_reduce_76, 3, 94, :_reduce_77, 3, 112, :_reduce_78, 3, 114, :_reduce_79, 1, 115, :_reduce_none, 1, 115, :_reduce_none, 0, 97, :_reduce_82, 1, 97, :_reduce_83, 3, 97, :_reduce_84, 1, 116, :_reduce_none, 3, 116, :_reduce_86, 1, 105, :_reduce_none, 1, 105, :_reduce_none, 1, 105, :_reduce_none, 1, 105, :_reduce_none, 1, 105, :_reduce_none, 1, 105, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 1, 113, :_reduce_none, 4, 87, :_reduce_105, 3, 87, :_reduce_106, 1, 89, :_reduce_107, 1, 89, :_reduce_108, 1, 86, :_reduce_109, 4, 82, :_reduce_110, 4, 82, :_reduce_111, 6, 72, :_reduce_112, 5, 72, :_reduce_113, 0, 119, :_reduce_none, 4, 119, :_reduce_115, 3, 119, :_reduce_116, 1, 110, :_reduce_none, 3, 110, :_reduce_118, 3, 110, :_reduce_119, 3, 110, :_reduce_120, 3, 110, :_reduce_121, 3, 110, :_reduce_122, 3, 110, :_reduce_123, 3, 110, :_reduce_124, 3, 110, :_reduce_125, 2, 110, :_reduce_126, 3, 110, :_reduce_127, 3, 110, :_reduce_128, 3, 110, :_reduce_129, 3, 110, :_reduce_130, 3, 110, :_reduce_131, 3, 110, :_reduce_132, 2, 110, :_reduce_133, 3, 110, :_reduce_134, 3, 110, :_reduce_135, 3, 110, :_reduce_136, 5, 71, :_reduce_137, 1, 121, :_reduce_none, 2, 121, :_reduce_139, 5, 122, :_reduce_140, 4, 122, :_reduce_141, 1, 123, :_reduce_none, 3, 123, :_reduce_143, 3, 88, :_reduce_144, 1, 125, :_reduce_none, 4, 125, :_reduce_146, 1, 127, :_reduce_none, 3, 127, :_reduce_148, 3, 126, :_reduce_149, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_none, 1, 124, :_reduce_157, 1, 124, :_reduce_none, 1, 128, :_reduce_159, 3, 128, :_reduce_160, 2, 73, :_reduce_161, 6, 75, :_reduce_162, 5, 75, :_reduce_163, 6, 76, :_reduce_164, 5, 76, :_reduce_165, 6, 77, :_reduce_166, 5, 77, :_reduce_167, 1, 96, :_reduce_168, 1, 91, :_reduce_169, 1, 91, :_reduce_170, 1, 131, :_reduce_none, 3, 131, :_reduce_172, 1, 133, :_reduce_173, 1, 134, :_reduce_174, 1, 134, :_reduce_175, 1, 134, :_reduce_176, 1, 134, :_reduce_177, 1, 134, :_reduce_none, 0, 65, :_reduce_179, 0, 135, :_reduce_180, 1, 129, :_reduce_none, 3, 129, :_reduce_182, 3, 129, :_reduce_183, 1, 136, :_reduce_none, 3, 136, :_reduce_185, 3, 137, :_reduce_186, 1, 137, :_reduce_187, 3, 137, :_reduce_188, 1, 137, :_reduce_189, 1, 132, :_reduce_none, 2, 132, :_reduce_191, 1, 130, :_reduce_none, 2, 130, :_reduce_193, 1, 138, :_reduce_none, 1, 138, :_reduce_none, 1, 84, :_reduce_196, 3, 109, :_reduce_197, 4, 109, :_reduce_198, 2, 109, :_reduce_199, 1, 117, :_reduce_none, 1, 117, :_reduce_none, 0, 95, :_reduce_none, 1, 95, :_reduce_203, 1, 120, :_reduce_204, 3, 118, :_reduce_205, 4, 118, :_reduce_206, 2, 118, :_reduce_207, 1, 139, :_reduce_none, 3, 139, :_reduce_209, 3, 140, :_reduce_210, 1, 141, :_reduce_211, 1, 141, :_reduce_212, 1, 141, :_reduce_213, 4, 111, :_reduce_214, 1, 90, :_reduce_none, 4, 90, :_reduce_216 ] racc_reduce_n = 217 racc_shift_n = 358 racc_action_table = [ 82, 61, 64, 317, 87, 172, 173, 211, 187, 308, 276, 328, 42, 43, 330, 211, 121, 283, 275, 186, 78, 82, 61, 64, 191, 87, 101, 329, 272, 273, 102, 51, -153, 42, 43, 63, 66, 42, 43, 71, 174, 119, -150, 103, 58, 84, 288, 44, 68, 215, 46, 92, 51, 57, 172, 173, 63, 66, 175, 45, 71, 149, 82, 61, 64, 58, 87, 104, 44, 68, 45, 46, 44, 150, 303, 46, 274, 219, 314, 211, 45, 141, 78, 82, 61, 64, 313, 87, 34, 174, 218, 45, 150, 51, -152, 45, 291, 63, 66, 35, 36, 71, 184, 78, 272, 273, 58, 84, 163, 187, 68, 184, 259, 92, 51, 260, 154, 38, 63, 66, 186, 45, 71, 38, 4, 191, 204, 58, 84, -154, 4, 68, 295, 38, 92, 82, 61, 64, 141, 87, 4, 9, 45, 187, 166, 167, 168, 152, 153, 155, -155, 160, 164, 187, 186, 78, 82, 61, 64, 191, 87, 38, 278, 298, 186, -151, 51, 281, 4, 191, 63, 66, 168, 152, 71, 300, 78, 160, 164, 58, 84, 163, 150, 68, 101, 278, 92, 51, -151, 154, 281, 63, 66, 261, 45, 71, -156, 163, 221, -155, 58, 84, 223, 224, 68, 154, -154, 92, 82, 61, 64, -153, 87, 61, 64, 45, -150, 166, 167, 168, 152, 153, 155, 180, 160, 164, 160, 164, 78, 57, 61, 64, 149, 166, 167, 168, 152, 153, 155, 51, 160, 164, 148, 63, 66, -168, 163, 71, 45, 53, 61, 64, 58, 84, 154, 304, 68, 168, 152, 92, 51, -152, 160, 164, 63, 66, 277, 45, 71, 181, 272, 273, 55, 58, 82, 61, 64, 68, 87, 223, 340, 182, 166, 167, 168, 152, 153, 155, 45, 160, 164, 160, 164, 184, 78, 82, 61, 64, 307, 87, 166, 167, 168, 152, 45, 51, 145, 160, 164, 63, 66, 257, 34, 71, 197, 223, 258, 217, 58, 84, 316, 198, 68, 268, 102, 92, 51, 223, 224, 141, 63, 66, 201, 45, 71, 327, 82, 61, 64, 58, 87, 213, 205, 68, 331, 114, 82, 61, 64, 206, 87, -152, 213, 107, 45, 166, 167, 168, 152, 153, 155, 210, 160, 164, 341, 304, 78, 51, 343, -150, 345, 63, 66, -152, -150, 71, -153, 51, -151, -155, 58, 63, 66, 105, 68, 71, 217, 213, 50, 49, 58, 84, 332, 351, 68, 45, -203, 92, 82, 61, 64, 39, 87, 214, -151, 45, -153, nil, 82, 61, 64, nil, 87, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, 78, 51, 4, 9, nil, 63, 66, nil, nil, 71, nil, 51, nil, nil, 58, 63, 66, nil, 68, 71, nil, nil, nil, nil, 58, 84, 352, nil, 68, 45, nil, 92, 82, 61, 64, nil, 87, nil, nil, 45, nil, nil, 82, 61, 64, nil, 87, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, 78, 51, 4, 9, nil, 63, 66, nil, nil, 71, nil, 51, nil, nil, 58, 63, 66, nil, 68, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, 45, nil, 92, 82, 61, 64, nil, 87, nil, nil, 45, 166, 167, 168, 152, nil, nil, nil, 160, 164, nil, nil, nil, 78, 82, 61, 64, nil, 87, nil, nil, nil, nil, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 66, nil, 45, 71, nil, 82, 61, 64, 58, 87, nil, nil, 68, 166, 167, 168, 152, 153, 155, nil, 160, 164, nil, 45, nil, 78, 82, 61, 64, nil, 87, nil, nil, nil, nil, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 66, nil, 45, 71, nil, 82, 61, 64, 58, 87, nil, nil, 68, nil, nil, 82, 61, 64, nil, 87, nil, nil, nil, 45, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 78, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, 51, nil, nil, 58, 63, 66, nil, 68, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, 45, nil, 92, 82, 61, 64, nil, 87, nil, nil, 45, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 78, 82, 61, 64, nil, nil, nil, nil, nil, nil, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 128, nil, 45, 71, nil, 82, 61, 64, 126, 87, nil, nil, 68, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 45, nil, 78, 82, 61, 64, nil, 87, nil, nil, nil, nil, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, 78, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 66, nil, 45, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 82, 61, 64, nil, 87, nil, nil, 45, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 78, 82, 61, 64, nil, 87, nil, nil, nil, nil, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 66, nil, 45, 71, nil, 82, 61, 64, 58, 87, nil, nil, 68, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 45, nil, 78, 82, 61, 64, nil, 87, nil, 82, 61, 64, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 66, 51, 45, 71, nil, 63, 229, nil, 58, 71, nil, nil, 68, nil, 126, 82, 61, 64, 68, 87, nil, nil, nil, 45, nil, nil, nil, nil, nil, 45, nil, nil, nil, nil, nil, 78, 82, 61, 64, 169, 87, nil, nil, nil, nil, nil, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, nil, nil, 92, 51, nil, nil, nil, 63, 66, nil, 45, 71, nil, 82, 61, 64, 58, 87, nil, nil, 68, nil, nil, 82, 61, 64, nil, 87, nil, nil, nil, 45, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 78, 51, nil, nil, nil, 63, 66, nil, nil, 71, nil, 51, nil, nil, 58, 63, 66, nil, 68, 71, nil, nil, nil, nil, 58, 84, nil, nil, 68, 45, nil, 92, 82, 61, 64, nil, 87, nil, nil, 45, nil, nil, 82, 61, 64, nil, 87, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 78, 51, 61, 64, nil, 63, 66, nil, nil, 71, nil, 51, nil, nil, 58, 63, 66, nil, 68, 71, nil, 61, 64, nil, 58, 84, nil, nil, 68, 45, nil, 92, 186, nil, nil, nil, 63, 229, nil, 45, 71, 297, 61, 64, nil, 126, nil, nil, nil, 68, nil, 51, 61, 64, nil, 63, 66, nil, nil, 71, 45, nil, nil, 55, 58, nil, nil, nil, 68, nil, nil, nil, 186, 61, 64, nil, 63, 229, nil, 45, 71, nil, 186, 61, 64, 126, 63, 229, nil, 68, 71, nil, nil, nil, nil, 126, nil, nil, nil, 68, 45, nil, nil, 186, 61, 64, nil, 63, 229, nil, 45, 71, nil, 186, nil, nil, 126, 63, 229, nil, 68, 71, 61, 64, nil, 231, 126, nil, nil, nil, 68, 45, nil, nil, 51, nil, 61, 64, 63, 66, nil, 45, 71, nil, nil, nil, 55, 58, nil, nil, nil, 68, 186, nil, 61, 64, 63, 229, 312, nil, 71, nil, 45, nil, nil, 126, 186, nil, nil, 68, 63, 229, nil, nil, 71, 61, 64, nil, nil, 126, 45, nil, nil, 68, 186, nil, nil, nil, 63, 229, nil, nil, 71, nil, 45, 236, nil, 126, nil, nil, nil, 68, nil, nil, nil, 51, 61, 64, nil, 63, 66, nil, 45, 71, nil, nil, nil, 55, 58, nil, nil, nil, 68, nil, nil, nil, 143, nil, nil, nil, nil, nil, nil, 45, nil, nil, 51, 165, nil, nil, 63, 66, nil, nil, 71, nil, 158, 163, 55, 58, nil, nil, nil, 68, nil, 154, 156, 159, 162, 158, 163, nil, nil, nil, 45, nil, nil, nil, 154, 156, 159, 162, nil, nil, nil, nil, nil, nil, nil, 157, 161, nil, nil, 166, 167, 168, 152, 153, 155, nil, 160, 164, 157, 161, nil, nil, 166, 167, 168, 152, 153, 155, nil, 160, 164, 158, 163, nil, nil, nil, nil, nil, nil, nil, 154, 156, 159, 162, 158, 163, nil, nil, nil, nil, nil, nil, nil, 154, 156, 159, 162, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 166, 167, 168, 152, 153, 155, nil, 160, 164, nil, 161, nil, nil, 166, 167, 168, 152, 153, 155, nil, 160, 164, 158, 163, nil, nil, nil, nil, nil, nil, nil, 154, 156, 159, 162, 158, 163, nil, nil, nil, nil, nil, nil, 238, 154, 156, 159, 162, nil, nil, nil, nil, nil, nil, nil, 157, 161, nil, nil, 166, 167, 168, 152, 153, 155, nil, 160, 164, 157, 161, nil, nil, 166, 167, 168, 152, 153, 155, 354, 160, 164, 158, 163, nil, nil, nil, nil, nil, nil, nil, 154, 156, 159, 162, nil, nil, nil, nil, nil, 19, nil, 23, 27, nil, 1, 5, 335, 12, nil, 17, nil, 21, nil, 28, 157, 161, 4, 9, 166, 167, 168, 152, 153, 155, nil, 160, 164, 19, 334, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, nil, 349, nil, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, 19, 252, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, nil, 306, nil, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, 19, 284, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, nil, 286, nil, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, 19, 356, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, nil, 357, nil, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9, 19, nil, 23, 27, nil, 1, 5, nil, 12, nil, 17, nil, 21, nil, 28, nil, nil, 4, 9 ] racc_action_check = [ 92, 92, 92, 270, 92, 260, 260, 141, 187, 260, 192, 279, 12, 12, 280, 184, 48, 199, 192, 187, 92, 145, 145, 145, 187, 145, 24, 280, 270, 270, 24, 92, 89, 119, 119, 92, 92, 121, 121, 92, 260, 48, 88, 24, 92, 92, 209, 12, 92, 141, 12, 92, 145, 66, 87, 87, 145, 145, 87, 92, 145, 237, 152, 152, 152, 145, 152, 24, 119, 145, 12, 119, 121, 66, 237, 121, 188, 144, 264, 128, 145, 182, 152, 153, 153, 153, 264, 153, 1, 87, 144, 119, 128, 152, 81, 121, 212, 152, 152, 1, 1, 152, 213, 153, 188, 188, 152, 152, 245, 104, 152, 214, 176, 152, 153, 176, 245, 27, 153, 153, 104, 152, 153, 114, 27, 104, 114, 153, 153, 77, 114, 153, 216, 31, 153, 154, 154, 154, 217, 154, 31, 31, 153, 271, 245, 245, 245, 245, 245, 245, 75, 245, 245, 103, 271, 154, 155, 155, 155, 271, 155, 5, 330, 220, 103, 73, 154, 330, 5, 103, 154, 154, 255, 255, 154, 226, 155, 255, 255, 154, 154, 243, 229, 154, 72, 198, 154, 155, 91, 243, 198, 155, 155, 178, 154, 155, 93, 246, 147, 94, 155, 155, 147, 147, 155, 246, 95, 155, 156, 156, 156, 70, 156, 23, 23, 155, 69, 243, 243, 243, 243, 243, 243, 97, 243, 243, 239, 239, 156, 17, 17, 17, 62, 246, 246, 246, 246, 246, 246, 156, 246, 246, 60, 156, 156, 58, 249, 156, 164, 17, 181, 181, 156, 156, 249, 252, 156, 254, 254, 156, 17, 56, 254, 254, 17, 17, 196, 156, 17, 99, 196, 196, 17, 17, 19, 19, 19, 17, 19, 302, 302, 100, 249, 249, 249, 249, 249, 249, 17, 249, 249, 256, 256, 102, 19, 21, 21, 21, 258, 21, 240, 240, 240, 240, 160, 19, 54, 240, 240, 19, 19, 170, 51, 19, 105, 170, 170, 267, 19, 19, 269, 106, 19, 183, 110, 19, 21, 183, 183, 50, 21, 21, 111, 19, 21, 278, 331, 331, 331, 21, 331, 185, 115, 21, 281, 37, 157, 157, 157, 118, 157, 123, 293, 28, 21, 241, 241, 241, 241, 241, 241, 127, 241, 241, 304, 306, 157, 331, 313, 129, 315, 331, 331, 320, 321, 331, 323, 157, 324, 325, 331, 157, 157, 26, 331, 157, 142, 130, 16, 13, 157, 157, 283, 339, 157, 331, 340, 157, 327, 327, 327, 6, 327, 134, 133, 157, 131, nil, 158, 158, 158, nil, 158, 283, nil, 283, 283, nil, 283, 283, nil, 283, nil, 283, nil, 283, nil, 283, 158, 327, 283, 283, nil, 327, 327, nil, nil, 327, nil, 158, nil, nil, 327, 158, 158, nil, 327, 158, nil, nil, nil, nil, 158, 158, 341, nil, 158, 327, nil, 158, 300, 300, 300, nil, 300, nil, nil, 158, nil, nil, 159, 159, 159, nil, 159, 341, nil, 341, 341, nil, 341, 341, nil, 341, nil, 341, nil, 341, nil, 341, 159, 300, 341, 341, nil, 300, 300, nil, nil, 300, nil, 159, nil, nil, 300, 159, 159, nil, 300, 159, nil, nil, nil, nil, 159, 159, nil, nil, 159, 300, nil, 159, 161, 161, 161, nil, 161, nil, nil, 159, 242, 242, 242, 242, nil, nil, nil, 242, 242, nil, nil, nil, 161, 34, 34, 34, nil, 34, nil, nil, nil, nil, nil, 161, nil, nil, nil, 161, 161, nil, nil, 161, nil, nil, nil, nil, 161, 161, nil, nil, 161, nil, nil, 161, 34, nil, nil, nil, 34, 34, nil, 161, 34, nil, 35, 35, 35, 34, 35, nil, nil, 34, 250, 250, 250, 250, 250, 250, nil, 250, 250, nil, 34, nil, 35, 222, 222, 222, nil, 222, nil, nil, nil, nil, nil, 35, nil, nil, nil, 35, 35, nil, nil, 35, nil, nil, nil, nil, 35, 35, nil, nil, 35, nil, nil, 35, 222, nil, nil, nil, 222, 222, nil, 35, 222, nil, 101, 101, 101, 222, 101, nil, nil, 222, nil, nil, 39, 39, 39, nil, 39, nil, nil, nil, 222, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 39, 101, nil, nil, nil, 101, 101, nil, nil, 101, nil, 39, nil, nil, 101, 39, 39, nil, 101, 39, nil, nil, nil, nil, 39, 39, nil, nil, 39, 101, nil, 39, 162, 162, 162, nil, 162, nil, nil, 39, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 162, 49, 49, 49, nil, nil, nil, nil, nil, nil, nil, 162, nil, nil, nil, 162, 162, nil, nil, 162, nil, nil, nil, nil, 162, 162, nil, nil, 162, nil, nil, 162, 49, nil, nil, nil, 49, 49, nil, 162, 49, nil, 163, 163, 163, 49, 163, nil, nil, 49, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 49, nil, 163, 166, 166, 166, nil, 166, nil, nil, nil, nil, nil, 163, nil, nil, nil, 163, 163, nil, nil, 163, nil, 166, nil, nil, 163, 163, nil, nil, 163, nil, nil, 163, 166, nil, nil, nil, 166, 166, nil, 163, 166, nil, nil, nil, nil, 166, 166, nil, nil, 166, nil, nil, 166, 167, 167, 167, nil, 167, nil, nil, 166, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 167, 261, 261, 261, nil, 261, nil, nil, nil, nil, nil, 167, nil, nil, nil, 167, 167, nil, nil, 167, nil, nil, nil, nil, 167, 167, nil, nil, 167, nil, nil, 167, 261, nil, nil, nil, 261, 261, nil, 167, 261, nil, 168, 168, 168, 261, 168, nil, nil, 261, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 261, nil, 168, 57, 57, 57, nil, 57, nil, 210, 210, 210, nil, 168, nil, nil, nil, 168, 168, nil, nil, 168, nil, nil, nil, nil, 168, 168, nil, nil, 168, nil, nil, 168, 57, nil, nil, nil, 57, 57, 210, 168, 57, nil, 210, 210, nil, 57, 210, nil, nil, 57, nil, 210, 84, 84, 84, 210, 84, nil, nil, nil, 57, nil, nil, nil, nil, nil, 210, nil, nil, nil, nil, nil, 84, 82, 82, 82, 82, 82, nil, nil, nil, nil, nil, 84, nil, nil, nil, 84, 84, nil, nil, 84, nil, nil, nil, nil, 84, 84, nil, nil, 84, nil, nil, 84, 82, nil, nil, nil, 82, 82, nil, 84, 82, nil, 211, 211, 211, 82, 211, nil, nil, 82, nil, nil, 78, 78, 78, nil, 78, nil, nil, nil, 82, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 78, 211, nil, nil, nil, 211, 211, nil, nil, 211, nil, 78, nil, nil, 211, 78, 78, nil, 211, 78, nil, nil, nil, nil, 78, 78, nil, nil, 78, 211, nil, 78, 215, 215, 215, nil, 215, nil, nil, 78, nil, nil, 36, 36, 36, nil, 36, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 36, 215, 231, 231, nil, 215, 215, nil, nil, 215, nil, 36, nil, nil, 215, 36, 36, nil, 215, 36, nil, 219, 219, nil, 36, 36, nil, nil, 36, 215, nil, 36, 231, nil, nil, nil, 231, 231, nil, 36, 231, 219, 338, 338, nil, 231, nil, nil, nil, 231, nil, 219, 314, 314, nil, 219, 219, nil, nil, 219, 231, nil, nil, 219, 219, nil, nil, nil, 219, nil, nil, nil, 338, 276, 276, nil, 338, 338, nil, 219, 338, nil, 314, 275, 275, 338, 314, 314, nil, 338, 314, nil, nil, nil, nil, 314, nil, nil, nil, 314, 338, nil, nil, 276, 149, 149, nil, 276, 276, nil, 314, 276, nil, 275, nil, nil, 276, 275, 275, nil, 276, 275, 148, 148, nil, 148, 275, nil, nil, nil, 275, 276, nil, nil, 149, nil, 180, 180, 149, 149, nil, 275, 149, nil, nil, nil, 149, 149, nil, nil, nil, 149, 148, nil, 262, 262, 148, 148, 262, nil, 148, nil, 149, nil, nil, 148, 180, nil, nil, 148, 180, 180, nil, nil, 180, 150, 150, nil, nil, 180, 148, nil, nil, 180, 262, nil, nil, nil, 262, 262, nil, nil, 262, nil, 180, 150, nil, 262, nil, nil, nil, 262, nil, nil, nil, 150, 53, 53, nil, 150, 150, nil, 262, 150, nil, nil, nil, 150, 150, nil, nil, nil, 150, nil, nil, nil, 53, nil, nil, nil, nil, nil, nil, 150, nil, nil, 53, 79, nil, nil, 53, 53, nil, nil, 53, nil, 79, 79, 53, 53, nil, nil, nil, 53, nil, 79, 79, 79, 79, 113, 113, nil, nil, nil, 53, nil, nil, nil, 113, 113, 113, 113, nil, nil, nil, nil, nil, nil, nil, 79, 79, nil, nil, 79, 79, 79, 79, 79, 79, nil, 79, 79, 113, 113, nil, nil, 113, 113, 113, 113, 113, 113, nil, 113, 113, 248, 248, nil, nil, nil, nil, nil, nil, nil, 248, 248, 248, 248, 244, 244, nil, nil, nil, nil, nil, nil, nil, 244, 244, 244, 244, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 248, 248, 248, 248, 248, 248, nil, 248, 248, nil, 244, nil, nil, 244, 244, 244, 244, 244, 244, nil, 244, 244, 112, 112, nil, nil, nil, nil, nil, nil, nil, 112, 112, 112, 112, 151, 151, nil, nil, nil, nil, nil, nil, 151, 151, 151, 151, 151, nil, nil, nil, nil, nil, nil, nil, 112, 112, nil, nil, 112, 112, 112, 112, 112, 112, nil, 112, 112, 151, 151, nil, nil, 151, 151, 151, 151, 151, 151, 343, 151, 151, 117, 117, nil, nil, nil, nil, nil, nil, nil, 117, 117, 117, 117, nil, nil, nil, nil, nil, 343, nil, 343, 343, nil, 343, 343, 287, 343, nil, 343, nil, 343, nil, 343, 117, 117, 343, 343, 117, 117, 117, 117, 117, 117, nil, 117, 117, 287, 285, 287, 287, nil, 287, 287, nil, 287, nil, 287, nil, 287, nil, 287, nil, nil, 287, 287, nil, 333, nil, 285, nil, 285, 285, nil, 285, 285, nil, 285, nil, 285, nil, 285, nil, 285, nil, nil, 285, 285, 333, 165, 333, 333, nil, 333, 333, nil, 333, nil, 333, nil, 333, nil, 333, nil, nil, 333, 333, nil, 253, nil, 165, nil, 165, 165, nil, 165, 165, nil, 165, nil, 165, nil, 165, nil, 165, nil, nil, 165, 165, 253, 205, 253, 253, nil, 253, 253, nil, 253, nil, 253, nil, 253, nil, 253, nil, nil, 253, 253, nil, 206, nil, 205, nil, 205, 205, nil, 205, 205, nil, 205, nil, 205, nil, 205, nil, 205, nil, nil, 205, 205, 206, 353, 206, 206, nil, 206, 206, nil, 206, nil, 206, nil, 206, nil, 206, nil, nil, 206, 206, nil, 355, nil, 353, nil, 353, 353, nil, 353, 353, nil, 353, nil, 353, nil, 353, nil, 353, nil, nil, 353, 353, 355, nil, 355, 355, nil, 355, 355, nil, 355, nil, 355, nil, 355, nil, 355, nil, nil, 355, 355, 0, nil, 0, 0, nil, 0, 0, nil, 0, nil, 0, nil, 0, nil, 0, nil, nil, 0, 0, 30, nil, 30, 30, nil, 30, 30, nil, 30, nil, 30, nil, 30, nil, 30, nil, nil, 30, 30 ] racc_action_pointer = [ 1755, 86, nil, nil, nil, 123, 388, nil, nil, nil, nil, nil, 9, 383, nil, nil, 382, 227, nil, 272, nil, 293, nil, 210, 24, nil, 383, 79, 311, nil, 1774, 95, nil, nil, 541, 582, 1109, 310, nil, 654, nil, nil, nil, nil, nil, nil, nil, nil, 6, 727, 291, 310, nil, 1334, 304, nil, 240, 924, 243, nil, 221, nil, 222, nil, nil, nil, 51, nil, nil, 195, 190, nil, 182, 144, nil, 129, nil, 108, 1047, 1362, nil, 73, 996, nil, 975, nil, nil, 51, 21, 11, nil, 167, -2, 175, 178, 185, nil, 217, nil, 259, 275, 644, 255, 131, 87, 314, 299, nil, nil, nil, 318, 327, 1480, 1375, 85, 336, nil, 1539, 343, 30, nil, 34, nil, 330, nil, nil, nil, 322, 70, 348, 377, 385, nil, 383, 385, nil, nil, nil, nil, nil, nil, -2, 376, nil, 67, 19, nil, 193, 1250, 1232, 1303, 1493, 60, 81, 133, 154, 206, 344, 406, 468, 243, 520, 706, 768, 187, 1635, 789, 841, 903, nil, 306, nil, nil, nil, nil, nil, 105, nil, 184, nil, 1264, 247, 43, 318, 6, 331, nil, -14, 56, nil, nil, nil, -6, nil, nil, nil, 222, nil, 152, 11, nil, nil, nil, nil, nil, 1676, 1695, nil, nil, 39, 930, 1037, 89, 64, 73, 1099, 125, 100, nil, 1149, 158, nil, 603, nil, nil, nil, 166, nil, nil, 160, nil, 1130, nil, nil, nil, nil, nil, 51, nil, 167, 248, 303, 478, 165, 1434, 92, 181, nil, 1421, 230, 540, nil, 226, 1654, 203, 118, 232, nil, 293, nil, 2, 862, 1282, nil, 68, nil, nil, 307, nil, 313, -20, 121, nil, nil, nil, 1211, 1201, nil, 322, -12, 4, 331, nil, 385, nil, 1594, nil, 1572, nil, nil, nil, nil, nil, 342, nil, nil, nil, nil, nil, nil, 458, nil, 270, nil, 358, nil, 336, nil, nil, nil, nil, nil, nil, 362, 1180, 363, nil, nil, nil, nil, 352, 353, nil, 355, 357, 358, nil, 396, nil, nil, 129, 334, nil, 1613, nil, nil, nil, nil, 1170, 386, 389, 447, nil, 1544, nil, nil, nil, nil, nil, nil, nil, nil, nil, 1717, nil, 1736, nil, nil ] racc_action_default = [ -179, -217, -15, -3, -170, -217, -217, -16, -5, -168, -17, -6, -217, -217, -7, -8, -217, -169, -9, -217, -10, -217, -11, -217, -217, -12, -217, -217, -40, -13, -1, -217, -14, -2, -217, -217, -217, -179, -169, -217, -171, -173, -176, -175, -174, -204, -177, -178, -179, -75, -82, -196, -31, -217, -215, -34, -32, -217, -64, -33, -217, -107, -21, -109, -108, -22, -63, -23, -62, -26, -27, -157, -217, -28, -156, -29, -158, -30, -217, -217, -97, -93, -217, -101, -217, -117, -102, -217, -94, -98, -100, -95, -217, -104, -96, -103, -99, -217, -159, -161, -43, -217, -75, -179, -179, -217, -179, -41, -4, -39, -217, -217, -72, -74, -217, -217, -192, -73, -217, -217, -190, -217, -68, -65, -76, -71, -64, -60, -63, -66, -202, -69, -58, -67, -217, -155, -154, -70, -80, -81, -83, -217, -202, -20, -217, -217, -85, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -217, -199, -217, -133, -213, -212, -211, -207, -217, -208, -217, -126, -217, -217, -82, -217, -217, -202, -196, -179, -217, -47, -49, -63, -217, -56, -55, -46, -217, 358, -180, -217, -181, -214, -194, -193, -195, -217, -217, -191, -172, -217, -61, -217, -217, -203, -75, -217, -217, -203, -18, -217, -217, -110, -217, -200, -201, -152, -217, -144, -145, -63, -150, -217, -153, -151, -24, -25, -106, -217, -136, -122, -124, -128, -125, -130, -135, -132, -129, -118, -134, -131, -127, -119, -114, -217, -120, -121, -123, -197, -201, -205, -217, -217, -217, -138, -217, -142, -160, -202, -111, -217, -217, -217, -52, -51, -45, -217, -217, -44, -189, -217, -217, -187, -184, -217, -165, -217, -167, -217, -35, -59, -78, -36, -77, -202, -79, -38, -84, -19, -216, -86, -217, -147, -202, -105, -217, -113, -114, -198, -206, -209, -210, -139, -137, -217, -217, -217, -37, -50, -48, -91, -87, -88, -53, -92, -89, -90, -54, -217, -182, -183, -217, -217, -163, -217, -164, -166, -57, -149, -217, -217, -201, -217, -112, -217, -143, -42, -188, -185, -186, -162, -148, -146, -116, -217, -141, -217, -115, -140 ] racc_goto_table = [ 24, 228, 59, 6, 130, 108, 16, 226, 177, 30, 62, 132, 138, 40, 282, 137, 142, 212, 47, 263, 37, 188, 196, 67, 234, 33, 305, 147, 97, 216, 24, 110, 100, 6, 125, 296, 16, 262, 59, 265, 338, 111, 106, 322, 326, 52, 144, 109, 227, 127, 302, 99, 170, 199, 115, 48, 118, 185, 209, 67, 207, 279, 116, 280, 146, 318, 203, 176, 56, 26, nil, 183, 269, 120, 98, nil, nil, 122, nil, nil, 342, 52, nil, nil, 301, nil, nil, nil, nil, 146, 226, nil, nil, nil, 234, nil, nil, nil, nil, nil, 123, 311, nil, nil, 56, 270, nil, nil, 146, 77, nil, nil, nil, nil, nil, nil, nil, 74, nil, nil, nil, 265, 208, nil, nil, 47, nil, 47, nil, 202, nil, 200, nil, nil, 59, 59, nil, nil, nil, nil, nil, 136, nil, 237, 138, 77, 347, nil, 267, 74, nil, nil, 220, 74, 315, 235, 67, nil, nil, nil, nil, nil, nil, nil, nil, 24, 247, nil, 6, 293, 251, 16, 289, 344, 253, 292, 137, 52, 52, 138, 336, 309, nil, nil, nil, nil, nil, nil, nil, 339, nil, 350, nil, nil, nil, 125, nil, 226, nil, 225, 56, 56, nil, nil, 59, 24, 24, nil, 6, 6, nil, 16, 16, nil, 285, 287, nil, nil, 290, nil, nil, nil, 294, nil, nil, 235, nil, nil, 108, 299, nil, 225, 266, nil, nil, nil, nil, nil, 122, nil, 136, 77, 77, nil, nil, nil, nil, 52, 74, 74, 74, nil, nil, 24, nil, nil, 6, nil, nil, 16, 108, 123, 108, nil, 73, nil, nil, nil, 310, 75, 56, nil, 136, nil, nil, nil, nil, nil, nil, nil, 74, nil, 225, 24, 69, 24, 6, 24, 6, 16, 6, 16, 333, 16, nil, nil, 133, nil, nil, nil, 73, 135, 136, 319, 319, 75, nil, 337, 108, nil, 74, 77, nil, 225, nil, nil, 129, nil, nil, 74, 69, nil, nil, 136, nil, nil, 320, 320, 108, nil, 108, 74, nil, 24, 346, nil, 6, 70, 348, 16, nil, 24, nil, 24, 6, nil, 6, 16, nil, 16, 353, nil, 355, 24, 136, 24, 6, nil, 6, 16, nil, 16, 74, nil, nil, 225, nil, 136, 136, 131, 193, 193, nil, 70, nil, 74, 74, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 225, nil, nil, nil, nil, nil, 233, 73, 73, nil, nil, 135, 75, 75, nil, nil, nil, 136, nil, nil, nil, nil, nil, nil, nil, 74, 230, 69, 69, nil, nil, 79, nil, nil, 194, 194, nil, nil, 233, nil, nil, 136, nil, 135, nil, nil, nil, 112, 113, 74, nil, 117, nil, nil, nil, nil, nil, nil, 230, nil, nil, nil, nil, nil, nil, 193, nil, nil, 133, nil, nil, nil, nil, 135, nil, nil, nil, 73, nil, 232, 70, 70, 75, nil, nil, nil, nil, nil, 129, 233, 151, nil, nil, nil, 135, nil, 171, 69, nil, nil, nil, nil, nil, nil, 179, nil, nil, nil, nil, 230, nil, 232, nil, nil, nil, nil, nil, nil, 194, nil, 233, nil, nil, nil, nil, 135, nil, nil, nil, nil, nil, nil, nil, 324, 324, nil, nil, nil, 325, 325, 230, 131, nil, nil, nil, nil, nil, nil, nil, 193, 70, nil, nil, 321, 321, nil, nil, nil, nil, nil, nil, nil, 232, nil, 239, 240, 241, 242, 243, 244, 245, 246, 233, 248, 249, 250, nil, 135, 254, 255, 256, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 230, 232, nil, nil, 233, nil, nil, nil, nil, 135, 194, nil, nil, nil, 323, 323, nil, nil, nil, nil, nil, nil, nil, nil, 230, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 232, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 232 ] racc_goto_check = [ 34, 64, 28, 49, 32, 4, 20, 62, 78, 2, 18, 44, 50, 71, 75, 47, 35, 33, 58, 60, 29, 38, 38, 20, 19, 3, 57, 54, 51, 33, 34, 34, 37, 49, 28, 53, 20, 59, 28, 62, 55, 51, 29, 43, 43, 26, 18, 5, 63, 30, 65, 66, 54, 67, 68, 69, 70, 32, 31, 20, 72, 73, 3, 74, 51, 39, 76, 77, 27, 1, nil, 54, 33, 3, 27, nil, nil, 26, nil, nil, 57, 26, nil, nil, 64, nil, nil, nil, nil, 51, 62, nil, nil, nil, 19, nil, nil, nil, nil, nil, 27, 60, nil, nil, 27, 38, nil, nil, 51, 25, nil, nil, nil, nil, nil, nil, nil, 46, nil, nil, nil, 62, 71, nil, nil, 58, nil, 58, nil, 29, nil, 3, nil, nil, 28, 28, nil, nil, nil, nil, nil, 25, nil, 18, 50, 25, 75, nil, 35, 46, nil, nil, 51, 46, 33, 20, 20, nil, nil, nil, nil, nil, nil, nil, nil, 34, 58, nil, 49, 32, 58, 20, 44, 62, 2, 50, 47, 26, 26, 50, 33, 78, nil, nil, nil, nil, nil, nil, nil, 33, nil, 64, nil, nil, nil, 28, nil, 62, nil, 27, 27, 27, nil, nil, 28, 34, 34, nil, 49, 49, nil, 20, 20, nil, 2, 2, nil, nil, 51, nil, nil, nil, 51, nil, nil, 20, nil, nil, 4, 51, nil, 27, 27, nil, nil, nil, nil, nil, 26, nil, 25, 25, 25, nil, nil, nil, nil, 26, 46, 46, 46, nil, nil, 34, nil, nil, 49, nil, nil, 20, 4, 27, 4, nil, 23, nil, nil, nil, 51, 24, 27, nil, 25, nil, nil, nil, nil, nil, nil, nil, 46, nil, 27, 34, 21, 34, 49, 34, 49, 20, 49, 20, 2, 20, nil, nil, 23, nil, nil, nil, 23, 24, 25, 26, 26, 24, nil, 51, 4, nil, 46, 25, nil, 27, nil, nil, 21, nil, nil, 46, 21, nil, nil, 25, nil, nil, 27, 27, 4, nil, 4, 46, nil, 34, 51, nil, 49, 22, 51, 20, nil, 34, nil, 34, 49, nil, 49, 20, nil, 20, 2, nil, 2, 34, 25, 34, 49, nil, 49, 20, nil, 20, 46, nil, nil, 27, nil, 25, 25, 22, 21, 21, nil, 22, nil, 46, 46, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 27, nil, nil, nil, nil, nil, 23, 23, 23, nil, nil, 24, 24, 24, nil, nil, nil, 25, nil, nil, nil, nil, nil, nil, nil, 46, 21, 21, 21, nil, nil, 48, nil, nil, 22, 22, nil, nil, 23, nil, nil, 25, nil, 24, nil, nil, nil, 48, 48, 46, nil, 48, nil, nil, nil, nil, nil, nil, 21, nil, nil, nil, nil, nil, nil, 21, nil, nil, 23, nil, nil, nil, nil, 24, nil, nil, nil, 23, nil, 22, 22, 22, 24, nil, nil, nil, nil, nil, 21, 23, 48, nil, nil, nil, 24, nil, 48, 21, nil, nil, nil, nil, nil, nil, 48, nil, nil, nil, nil, 21, nil, 22, nil, nil, nil, nil, nil, nil, 22, nil, 23, nil, nil, nil, nil, 24, nil, nil, nil, nil, nil, nil, nil, 23, 23, nil, nil, nil, 24, 24, 21, 22, nil, nil, nil, nil, nil, nil, nil, 21, 22, nil, nil, 21, 21, nil, nil, nil, nil, nil, nil, nil, 22, nil, 48, 48, 48, 48, 48, 48, 48, 48, 23, 48, 48, 48, nil, 24, 48, 48, 48, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 21, 22, nil, nil, 23, nil, nil, nil, nil, 24, 22, nil, nil, nil, 22, 22, nil, nil, nil, nil, nil, nil, nil, nil, 21, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 22, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 22 ] racc_goto_pointer = [ nil, 69, 9, 25, -25, 16, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, -7, -125, 6, 267, 320, 247, 252, 92, 28, 51, -15, 15, 0, -69, -45, -113, 0, -34, nil, 8, -82, -206, nil, nil, nil, -232, -38, nil, 100, -34, 401, 3, -38, 7, nil, -182, -30, -262, nil, -226, 6, -143, -161, nil, -141, -100, -147, -181, 28, -53, 17, 43, 8, 1, -59, -137, -135, -184, -48, -20, -79, nil ] racc_goto_default = [ nil, nil, nil, 195, 3, 8, 11, 14, 15, 18, 20, 22, 25, 29, 32, 2, 7, 10, nil, 65, 86, 88, 89, 91, 94, 95, 80, 81, 83, 13, nil, nil, nil, nil, 72, nil, 31, nil, nil, 189, 271, 190, 192, nil, nil, 134, 93, 96, nil, 54, 124, 85, 139, 140, nil, 222, 90, nil, 76, nil, nil, 264, 60, nil, nil, nil, nil, nil, nil, nil, nil, nil, 41, nil, nil, nil, nil, nil, nil, 178 ] 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, :APPENDS => 14, :LESSEQUAL => 15, :NOTEQUAL => 16, :DOT => 17, :COLON => 18, :LLCOLLECT => 19, :RRCOLLECT => 20, :QMARK => 21, :LPAREN => 22, :RPAREN => 23, :ISEQUAL => 24, :GREATEREQUAL => 25, :GREATERTHAN => 26, :LESSTHAN => 27, :IF => 28, :ELSE => 29, :IMPORT => 30, :DEFINE => 31, :ELSIF => 32, :VARIABLE => 33, :CLASS => 34, :INHERITS => 35, :NODE => 36, :BOOLEAN => 37, :NAME => 38, :SEMIC => 39, :CASE => 40, :DEFAULT => 41, :AT => 42, :LCOLLECT => 43, :RCOLLECT => 44, :CLASSNAME => 45, :CLASSREF => 46, :NOT => 47, :OR => 48, :AND => 49, :UNDEF => 50, :PARROW => 51, :PLUS => 52, :MINUS => 53, :TIMES => 54, :DIV => 55, :LSHIFT => 56, :RSHIFT => 57, :UMINUS => 58, :MATCH => 59, :NOMATCH => 60, :REGEX => 61 } racc_use_result_var = true racc_nt_base = 62 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', 'APPENDS', 'LESSEQUAL', 'NOTEQUAL', 'DOT', 'COLON', 'LLCOLLECT', 'RRCOLLECT', 'QMARK', 'LPAREN', 'RPAREN', 'ISEQUAL', 'GREATEREQUAL', 'GREATERTHAN', 'LESSTHAN', 'IF', 'ELSE', 'IMPORT', 'DEFINE', 'ELSIF', 'VARIABLE', 'CLASS', 'INHERITS', 'NODE', 'BOOLEAN', 'NAME', 'SEMIC', 'CASE', 'DEFAULT', 'AT', 'LCOLLECT', 'RCOLLECT', 'CLASSNAME', 'CLASSREF', 'NOT', 'OR', 'AND', 'UNDEF', 'PARROW', 'PLUS', 'MINUS', 'TIMES', 'DIV', 'LSHIFT', 'RSHIFT', 'UMINUS', 'MATCH', 'NOMATCH', 'REGEX', '$start', 'program', 'statements', 'nil', 'statement', 'resource', 'virtualresource', 'collection', 'assignment', 'casestatement', 'ifstatement', 'import', 'fstatement', 'definition', 'hostclass', 'nodedef', 'resourceoverride', 'append', 'funcvalues', 'namestring', 'resourceref', 'name', 'variable', 'type', 'boolean', 'funcrvalue', 'selector', 'quotedtext', 'hasharrayaccesses', 'classname', 'resourceinstances', 'endsemi', 'params', 'endcomma', 'classref', 'anyparams', 'at', 'collectrhand', 'collstatements', 'collstatement', 'colljoin', 'collexpr', 'colllval', 'simplervalue', 'resourceinst', 'resourcename', 'undef', 'array', 'expression', 'hasharrayaccess', 'param', 'rvalue', 'addparam', 'anyparam', 'rvalues', 'comma', 'hash', 'else', 'regex', 'caseopts', 'caseopt', 'casevalues', 'selectlhand', 'svalues', 'selectval', 'sintvalues', 'qtexts', 'argumentlist', 'classparent', 'hostnames', 'nodeparent', 'nodename', 'hostname', 'nothing', 'arguments', 'argument', 'classnameordefault', 'hashpairs', 'hashpair', 'key'] Racc_debug_parser = false ##### racc system variables end ##### # reduce 0 omitted module_eval <<'.,.,', 'grammar.ra', 44 def _reduce_1( val, _values, result ) if val[0] # Make sure we always return an array. if val[0].is_a?(AST::ASTArray) if val[0].children.empty? result = nil else result = val[0] end else result = aryfy(val[0]) end else result = nil end result end .,., # reduce 2 omitted # reduce 3 omitted module_eval <<'.,.,', 'grammar.ra', 60 def _reduce_4( val, _values, result ) if val[0] and val[1] 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 elsif obj = (val[0] || val[1]) result = obj else result = nil 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 # reduce 16 omitted # reduce 17 omitted module_eval <<'.,.,', 'grammar.ra', 84 def _reduce_18( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :statement result end .,., module_eval <<'.,.,', 'grammar.ra', 92 def _reduce_19( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :statement result end .,., module_eval <<'.,.,', 'grammar.ra', 98 def _reduce_20( val, _values, result ) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :statement result end .,., module_eval <<'.,.,', 'grammar.ra', 106 def _reduce_21( val, _values, result ) args = aryfy(val[1]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :statement result end .,., # reduce 22 omitted # reduce 23 omitted module_eval <<'.,.,', 'grammar.ra', 114 def _reduce_24( val, _values, result ) result = aryfy(val[0], val[2]) result.line = @lexer.line result.file = @lexer.file result end .,., module_eval <<'.,.,', 'grammar.ra', 123 def _reduce_25( val, _values, result ) unless val[0].is_a?(AST::ASTArray) val[0] = aryfy(val[0]) end val[0].push(val[2]) result = val[0] result end .,., # reduce 26 omitted # reduce 27 omitted # reduce 28 omitted # reduce 29 omitted # reduce 30 omitted # reduce 31 omitted # reduce 32 omitted # reduce 33 omitted module_eval <<'.,.,', 'grammar.ra', 137 def _reduce_34( val, _values, result ) result = ast AST::Name, :value => val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 159 def _reduce_35( val, _values, result ) @lexer.commentpop array = val[2] if array.instance_of?(AST::ResourceInstance) array = [array] end result = ast AST::ASTArray # this iterates across each specified resourceinstance array.each { |instance| unless instance.instance_of?(AST::ResourceInstance) 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::Resource, :type => val[0], :title => instance[0], :params => instance[1]) } result end .,., module_eval <<'.,.,', 'grammar.ra', 162 def _reduce_36( val, _values, result ) # This is a deprecated syntax. error "All resource specifications require names" result end .,., module_eval <<'.,.,', 'grammar.ra', 165 def _reduce_37( val, _values, result ) # a defaults setting for a type result = ast(AST::ResourceDefaults, :type => val[0], :params => val[2]) result end .,., module_eval <<'.,.,', 'grammar.ra', 171 def _reduce_38( val, _values, result ) @lexer.commentpop result = ast AST::ResourceOverride, :object => val[0], :params => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 198 def _reduce_39( val, _values, result ) type = val[0] if (type == :exported and ! Puppet[:storeconfigs]) and ! Puppet[:parseonly] Puppet.warning addcontext("You cannot collect without storeconfigs being set") end if val[1].is_a? AST::ResourceDefaults error "Defaults are not virtualizable" end method = type.to_s + "=" # Just mark our resources as exported and pass them through. if val[1].instance_of?(AST::ASTArray) val[1].each do |obj| obj.send(method, true) end else val[1].send(method, true) end result = val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 199 def _reduce_40( val, _values, result ) result = :virtual result end .,., module_eval <<'.,.,', 'grammar.ra', 200 def _reduce_41( val, _values, result ) result = :exported result end .,., module_eval <<'.,.,', 'grammar.ra', 224 def _reduce_42( val, _values, result ) if val[0] =~ /^[a-z]/ Puppet.warning addcontext("Collection names must now be capitalized") end type = val[0].downcase args = {:type => type} if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end if args[:form] == :exported and ! Puppet[:storeconfigs] and ! Puppet[:parseonly] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end args[:override] = val[3] result = ast AST::Collection, args result end .,., module_eval <<'.,.,', 'grammar.ra', 243 def _reduce_43( val, _values, result ) if val[0] =~ /^[a-z]/ Puppet.warning addcontext("Collection names must now be capitalized") end type = val[0].downcase args = {:type => type } if val[1].is_a?(AST::CollExpr) args[:query] = val[1] args[:query].type = type args[:form] = args[:query].form else args[:form] = val[1] end if args[:form] == :exported and ! Puppet[:storeconfigs] and ! Puppet[:parseonly] Puppet.warning addcontext("You cannot collect exported resources without storeconfigs being set; the collection will be ignored") end result = ast AST::Collection, args result end .,., module_eval <<'.,.,', 'grammar.ra', 253 def _reduce_44( val, _values, result ) if val[1] result = val[1] result.form = :virtual else result = :virtual end result end .,., module_eval <<'.,.,', 'grammar.ra', 261 def _reduce_45( val, _values, result ) if val[1] result = val[1] result.form = :exported else result = :exported end result end .,., # reduce 46 omitted # reduce 47 omitted module_eval <<'.,.,', 'grammar.ra', 269 def _reduce_48( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1], :test2 => val[2] result end .,., # reduce 49 omitted module_eval <<'.,.,', 'grammar.ra', 275 def _reduce_50( val, _values, result ) result = val[1] result.parens = true result end .,., module_eval <<'.,.,', 'grammar.ra', 276 def _reduce_51( val, _values, result ) result=val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 277 def _reduce_52( val, _values, result ) result=val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 284 def _reduce_53( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., module_eval <<'.,.,', 'grammar.ra', 289 def _reduce_54( val, _values, result ) result = ast AST::CollExpr, :test1 => val[0], :oper => val[1][:value], :test2 => val[2] #result = ast AST::CollExpr #result.push *val result end .,., # reduce 55 omitted # reduce 56 omitted module_eval <<'.,.,', 'grammar.ra', 296 def _reduce_57( val, _values, result ) result = ast AST::ResourceInstance, :children => [val[0],val[2]] result end .,., # reduce 58 omitted module_eval <<'.,.,', 'grammar.ra', 306 def _reduce_59( val, _values, result ) if val[0].instance_of?(AST::ResourceInstance) result = ast AST::ASTArray, :children => [val[0],val[2]] else val[0].push val[2] result = val[0] end result end .,., # reduce 60 omitted # reduce 61 omitted module_eval <<'.,.,', 'grammar.ra', 313 def _reduce_62( val, _values, result ) result = ast AST::Undef, :value => :undef result end .,., module_eval <<'.,.,', 'grammar.ra', 317 def _reduce_63( val, _values, result ) result = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 321 def _reduce_64( val, _values, result ) result = ast AST::Type, :value => val[0][:value], :line => val[0][:line] result end .,., # reduce 65 omitted # reduce 66 omitted # reduce 67 omitted # reduce 68 omitted # reduce 69 omitted # reduce 70 omitted # reduce 71 omitted module_eval <<'.,.,', 'grammar.ra', 338 def _reduce_72( val, _values, result ) if val[0][:value] =~ /::/ raise Puppet::ParseError, "Cannot assign to variables in other namespaces" end # this is distinct from referencing a variable variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 341 def _reduce_73( val, _values, result ) result = ast AST::VarDef, :name => val[0], :value => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 346 def _reduce_74( val, _values, result ) variable = ast AST::Name, :value => val[0][:value], :line => val[0][:line] result = ast AST::VarDef, :name => variable, :value => val[2], :append => true, :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 351 def _reduce_75( val, _values, result ) result = ast AST::ASTArray result end .,., module_eval <<'.,.,', 'grammar.ra', 351 def _reduce_76( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 360 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', 364 def _reduce_78( val, _values, result ) result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 369 def _reduce_79( val, _values, result ) result = ast AST::ResourceParam, :param => val[0][:value], :line => val[0][:line], :value => val[2], :add => true result end .,., # reduce 80 omitted # reduce 81 omitted module_eval <<'.,.,', 'grammar.ra', 377 def _reduce_82( val, _values, result ) result = ast AST::ASTArray result end .,., module_eval <<'.,.,', 'grammar.ra', 377 def _reduce_83( val, _values, result ) result = val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 386 def _reduce_84( 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 .,., # reduce 85 omitted module_eval <<'.,.,', 'grammar.ra', 395 def _reduce_86( 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 87 omitted # reduce 88 omitted # reduce 89 omitted # reduce 90 omitted # reduce 91 omitted # reduce 92 omitted # reduce 93 omitted # reduce 94 omitted # reduce 95 omitted # reduce 96 omitted # reduce 97 omitted # reduce 98 omitted # reduce 99 omitted # reduce 100 omitted # reduce 101 omitted # reduce 102 omitted # reduce 103 omitted # reduce 104 omitted module_eval <<'.,.,', 'grammar.ra', 424 def _reduce_105( val, _values, result ) args = aryfy(val[2]) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => args, :ftype => :rvalue result end .,., module_eval <<'.,.,', 'grammar.ra', 429 def _reduce_106( val, _values, result ) result = ast AST::Function, :name => val[0][:value], :line => val[0][:line], :arguments => AST::ASTArray.new({}), :ftype => :rvalue result end .,., module_eval <<'.,.,', 'grammar.ra', 433 def _reduce_107( val, _values, result ) result = ast AST::String, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 435 def _reduce_108( val, _values, result ) result = ast AST::FlatString, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 439 def _reduce_109( val, _values, result ) result = ast AST::Boolean, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 444 def _reduce_110( val, _values, result ) Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized") result = ast AST::ResourceReference, :type => val[0][:value], :line => val[0][:line], :title => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 446 def _reduce_111( val, _values, result ) result = ast AST::ResourceReference, :type => val[0], :title => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 460 def _reduce_112( val, _values, result ) @lexer.commentpop args = { :test => val[1], :statements => val[3] } if val[5] args[:else] = val[5] end result = ast AST::IfStatement, args result end .,., module_eval <<'.,.,', 'grammar.ra', 473 def _reduce_113( val, _values, result ) @lexer.commentpop args = { :test => val[1], :statements => ast(AST::Nop) } if val[4] args[:else] = val[4] end result = ast AST::IfStatement, args result end .,., # reduce 114 omitted module_eval <<'.,.,', 'grammar.ra', 479 def _reduce_115( val, _values, result ) @lexer.commentpop result = ast AST::Else, :statements => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 483 def _reduce_116( val, _values, result ) @lexer.commentpop result = ast AST::Else, :statements => ast(AST::Nop) result end .,., # reduce 117 omitted module_eval <<'.,.,', 'grammar.ra', 500 def _reduce_118( val, _values, result ) result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 503 def _reduce_119( val, _values, result ) result = ast AST::MatchOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 506 def _reduce_120( val, _values, result ) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 509 def _reduce_121( val, _values, result ) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 512 def _reduce_122( val, _values, result ) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 515 def _reduce_123( val, _values, result ) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 518 def _reduce_124( val, _values, result ) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 521 def _reduce_125( val, _values, result ) result = ast AST::ArithmeticOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 524 def _reduce_126( val, _values, result ) result = ast AST::Minus, :value => val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 527 def _reduce_127( val, _values, result ) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 530 def _reduce_128( val, _values, result ) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 533 def _reduce_129( val, _values, result ) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 536 def _reduce_130( val, _values, result ) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 539 def _reduce_131( val, _values, result ) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 542 def _reduce_132( val, _values, result ) result = ast AST::ComparisonOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 545 def _reduce_133( val, _values, result ) result = ast AST::Not, :value => val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 548 def _reduce_134( val, _values, result ) result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 551 def _reduce_135( val, _values, result ) result = ast AST::BooleanOperator, :operator => val[1][:value], :lval => val[0], :rval => val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 554 def _reduce_136( val, _values, result ) result = val[1] result end .,., module_eval <<'.,.,', 'grammar.ra', 563 def _reduce_137( val, _values, result ) @lexer.commentpop 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 138 omitted module_eval <<'.,.,', 'grammar.ra', 573 def _reduce_139( 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', 578 def _reduce_140( val, _values, result ) @lexer.commentpop result = ast AST::CaseOpt, :value => val[0], :statements => val[3] result end .,., module_eval <<'.,.,', 'grammar.ra', 584 def _reduce_141( val, _values, result ) @lexer.commentpop result = ast(AST::CaseOpt, :value => val[0], :statements => ast(AST::ASTArray) ) result end .,., # reduce 142 omitted module_eval <<'.,.,', 'grammar.ra', 594 def _reduce_143( 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', 598 def _reduce_144( val, _values, result ) result = ast AST::Selector, :param => val[0], :values => val[2] result end .,., # reduce 145 omitted module_eval <<'.,.,', 'grammar.ra', 604 def _reduce_146( val, _values, result ) @lexer.commentpop result = val[1] result end .,., # reduce 147 omitted module_eval <<'.,.,', 'grammar.ra', 614 def _reduce_148( 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', 618 def _reduce_149( val, _values, result ) result = ast AST::ResourceParam, :param => val[0], :value => val[2] result end .,., # reduce 150 omitted # reduce 151 omitted # reduce 152 omitted # reduce 153 omitted # reduce 154 omitted # reduce 155 omitted # reduce 156 omitted module_eval <<'.,.,', 'grammar.ra', 629 def _reduce_157( val, _values, result ) result = ast AST::Default, :value => val[0][:value], :line => val[0][:line] result end .,., # reduce 158 omitted module_eval <<'.,.,', 'grammar.ra', 632 def _reduce_159( val, _values, result ) result = [val[0].value] result end .,., module_eval <<'.,.,', 'grammar.ra', 636 def _reduce_160( val, _values, result ) results = val[0] << val[2].value result end .,., module_eval <<'.,.,', 'grammar.ra', 644 def _reduce_161( val, _values, result ) val[1].each do |file| import(file) end result = AST::ASTArray.new(:children => []) result end .,., module_eval <<'.,.,', 'grammar.ra', 655 def _reduce_162( val, _values, result ) @lexer.commentpop newdefine classname(val[1]), :arguments => val[2], :code => val[4], :line => val[0][:line] @lexer.indefine = false result = nil #} | DEFINE NAME argumentlist parent LBRACE RBRACE { result end .,., module_eval <<'.,.,', 'grammar.ra', 660 def _reduce_163( val, _values, result ) @lexer.commentpop newdefine classname(val[1]), :arguments => val[2], :line => val[0][:line] @lexer.indefine = false result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 669 def _reduce_164( val, _values, result ) @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :code => val[4], :parent => val[2], :line => val[0][:line] result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 675 def _reduce_165( val, _values, result ) @lexer.commentpop # Our class gets defined in the parent namespace, not our own. @lexer.namepop newclass classname(val[1]), :parent => val[2], :line => val[0][:line] result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 681 def _reduce_166( val, _values, result ) @lexer.commentpop newnode val[1], :parent => val[2], :code => val[4], :line => val[0][:line] result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 685 def _reduce_167( val, _values, result ) @lexer.commentpop newnode val[1], :parent => val[2], :line => val[0][:line] result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 686 def _reduce_168( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 688 def _reduce_169( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 689 def _reduce_170( val, _values, result ) result = val[0][:value] result end .,., # reduce 171 omitted module_eval <<'.,.,', 'grammar.ra', 699 def _reduce_172( val, _values, result ) result = val[0] result = [result] unless result.is_a?(Array) result << val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 703 def _reduce_173( val, _values, result ) result = ast AST::HostName, :value => val[0] result end .,., module_eval <<'.,.,', 'grammar.ra', 704 def _reduce_174( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 705 def _reduce_175( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 706 def _reduce_176( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 707 def _reduce_177( val, _values, result ) result = val[0][:value] result end .,., # reduce 178 omitted module_eval <<'.,.,', 'grammar.ra', 713 def _reduce_179( val, _values, result ) result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 717 def _reduce_180( val, _values, result ) result = ast AST::ASTArray, :children => [] result end .,., # reduce 181 omitted module_eval <<'.,.,', 'grammar.ra', 722 def _reduce_182( val, _values, result ) result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 726 def _reduce_183( val, _values, result ) result = val[1] result = [result] unless result[0].is_a?(Array) result end .,., # reduce 184 omitted module_eval <<'.,.,', 'grammar.ra', 733 def _reduce_185( val, _values, result ) result = val[0] result = [result] unless result[0].is_a?(Array) result << val[2] result end .,., module_eval <<'.,.,', 'grammar.ra', 738 def _reduce_186( val, _values, result ) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value], val[2]] result end .,., module_eval <<'.,.,', 'grammar.ra', 742 def _reduce_187( val, _values, result ) Puppet.warning addcontext("Deprecation notice: must now include '$' in prototype") result = [val[0][:value]] result end .,., module_eval <<'.,.,', 'grammar.ra', 744 def _reduce_188( val, _values, result ) result = [val[0][:value], val[2]] result end .,., module_eval <<'.,.,', 'grammar.ra', 746 def _reduce_189( val, _values, result ) result = [val[0][:value]] result end .,., # reduce 190 omitted module_eval <<'.,.,', 'grammar.ra', 751 def _reduce_191( val, _values, result ) result = val[1] result end .,., # reduce 192 omitted module_eval <<'.,.,', 'grammar.ra', 756 def _reduce_193( val, _values, result ) result = val[1] result end .,., # reduce 194 omitted # reduce 195 omitted module_eval <<'.,.,', 'grammar.ra', 762 def _reduce_196( val, _values, result ) result = ast AST::Variable, :value => val[0][:value], :line => val[0][:line] result end .,., module_eval <<'.,.,', 'grammar.ra', 770 def _reduce_197( 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', 777 def _reduce_198( 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', 779 def _reduce_199( val, _values, result ) result = ast AST::ASTArray result end .,., # reduce 200 omitted # reduce 201 omitted # reduce 202 omitted module_eval <<'.,.,', 'grammar.ra', 784 def _reduce_203( val, _values, result ) result = nil result end .,., module_eval <<'.,.,', 'grammar.ra', 789 def _reduce_204( val, _values, result ) result = ast AST::Regex, :value => val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 797 def _reduce_205( val, _values, result ) if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end result end .,., module_eval <<'.,.,', 'grammar.ra', 804 def _reduce_206( val, _values, result ) if val[1].instance_of?(AST::ASTHash) result = val[1] else result = ast AST::ASTHash, { :value => val[1] } end result end .,., module_eval <<'.,.,', 'grammar.ra', 806 def _reduce_207( val, _values, result ) result = ast AST::ASTHash result end .,., # reduce 208 omitted module_eval <<'.,.,', 'grammar.ra', 816 def _reduce_209( val, _values, result ) if val[0].instance_of?(AST::ASTHash) result = val[0].merge(val[2]) else result = ast AST::ASTHash, :value => val[0] result.merge(val[2]) end result end .,., module_eval <<'.,.,', 'grammar.ra', 820 def _reduce_210( val, _values, result ) result = ast AST::ASTHash, { :value => { val[0] => val[2] } } result end .,., module_eval <<'.,.,', 'grammar.ra', 821 def _reduce_211( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 822 def _reduce_212( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 823 def _reduce_213( val, _values, result ) result = val[0][:value] result end .,., module_eval <<'.,.,', 'grammar.ra', 828 def _reduce_214( val, _values, result ) result = ast AST::HashOrArrayAccess, :variable => val[0][:value], :key => val[2] result end .,., # reduce 215 omitted module_eval <<'.,.,', 'grammar.ra', 833 def _reduce_216( val, _values, result ) result = ast AST::HashOrArrayAccess, :variable => val[0], :key => val[2] result end .,., def _reduce_none( val, _values, result ) result end end # class Parser end # module Parser end # module Puppet diff --git a/spec/integration/parser/functions/include.rb b/spec/integration/parser/functions/include.rb new file mode 100755 index 000000000..64346bffb --- /dev/null +++ b/spec/integration/parser/functions/include.rb @@ -0,0 +1,25 @@ +#!/usr/bin/env ruby + +require File.dirname(__FILE__) + '/../../../spec_helper' + +describe "The include function" do + before :each do + @node = Puppet::Node.new("mynode") + @compiler = Puppet::Parser::Compiler.new(@node) + @compiler.send(:evaluate_main) + @scope = @compiler.topscope + # preload our functions + Puppet::Parser::Functions.function(:include) + Puppet::Parser::Functions.function(:require) + end + + it "should add a containment relationship between the 'included' class and our class" do + @compiler.known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, "includedclass") + + @scope.function_include("includedclass") + + klass_resource = @compiler.findresource(:class,"includedclass") + klass_resource.should be_instance_of(Puppet::Parser::Resource) + @compiler.catalog.should be_edge(@scope.resource, klass_resource) + end +end diff --git a/spec/integration/parser/functions/require.rb b/spec/integration/parser/functions/require.rb index 6f169ade1..5f95ec520 100755 --- a/spec/integration/parser/functions/require.rb +++ b/spec/integration/parser/functions/require.rb @@ -1,86 +1,44 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../../spec_helper' -describe "the require function" do - +describe "The require function" do before :each do - @parser = Puppet::Parser::Parser.new :Code => "" @node = Puppet::Node.new("mynode") - @compiler = Puppet::Parser::Compiler.new(@node, @parser) + @compiler = Puppet::Parser::Compiler.new(@node) @compiler.send(:evaluate_main) @compiler.catalog.client_version = "0.25" @scope = @compiler.topscope # preload our functions Puppet::Parser::Functions.function(:include) Puppet::Parser::Functions.function(:require) end - it "should add a relationship between the 'required' class and our class" do - @parser.newclass("requiredclass") + it "should add a dependency between the 'required' class and our class" do + @compiler.known_resource_types.add Puppet::Parser::ResourceType.new(:hostclass, "requiredclass") @scope.function_require("requiredclass") @scope.resource["require"].should_not be_nil ref = @scope.resource["require"].shift ref.type.should == "Class" ref.title.should == "requiredclass" end it "should queue relationships between the 'required' class and our classes" do @parser.newclass("requiredclass1") @parser.newclass("requiredclass2") @scope.function_require("requiredclass1") @scope.function_require("requiredclass2") @scope.resource["require"].should_not be_nil (ref1,ref2) = @scope.resource["require"] ref1.type.should == "Class" ref1.title.should == "requiredclass1" ref2.type.should == "Class" ref2.title.should == "requiredclass2" end end - -describe "the include function" do - require 'puppet_spec/files' - include PuppetSpec::Files - - before :each do - @real_dir = Dir.getwd - @temp_dir = tmpfile('include_function_integration_test') - Dir.mkdir @temp_dir - Dir.chdir @temp_dir - @parser = Puppet::Parser::Parser.new :Code => "" - @node = Puppet::Node.new("mynode") - @compiler = Puppet::Parser::Compiler.new(@node, @parser) - @compiler.send(:evaluate_main) - @scope = @compiler.topscope - # preload our functions - Puppet::Parser::Functions.function(:include) - Puppet::Parser::Functions.function(:require) - end - - after :each do - Dir.chdir @real_dir - Dir.rmdir @temp_dir - end - - def with_file(filename,contents) - path = File.join(@temp_dir,filename) - File.open(path, "w") { |f|f.puts contents } - yield - File.delete(path) - end - - it "should add a relationship between the 'included' class and our class" do - with_file('includedclass',"class includedclass {}") { - @scope.function_include("includedclass") - } - @compiler.catalog.edge?(@scope.resource,@compiler.findresource(:class,"includedclass")).should be_true - end - -end diff --git a/spec/unit/application/main.rb b/spec/unit/application/main.rb index ef9626766..ca185cb0a 100755 --- a/spec/unit/application/main.rb +++ b/spec/unit/application/main.rb @@ -1,390 +1,384 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/main' describe "Puppet" do before :each do @main = Puppet::Application[:main] Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) end [:debug,:loadclasses,:verbose,:use_nodes,:detailed_exitcodes].each do |option| it "should declare handle_#{option} method" do @main.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @main.options.expects(:[]=).with(option, 'arg') @main.send("handle_#{option}".to_sym, 'arg') end end it "should set the code to the provided code when :execute is used" do @main.options.expects(:[]=).with(:code, 'arg') @main.send("handle_execute".to_sym, 'arg') end it "should ask Puppet::Application to parse Puppet configuration file" do @main.should_parse_config?.should be_true end describe "when applying options" do it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @main.handle_logdest("console") end it "should put the logset options to true" do @main.options.expects(:[]=).with(:logset,true) @main.handle_logdest("console") end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:trap) Puppet::Log.stubs(:level=) Puppet.stubs(:parse_config) Puppet::Network::Client.dipper.stubs(:new) STDIN.stubs(:read) @main.options.stubs(:[]).with(any_parameters) end it "should set show_diff on --noop" do Puppet.stubs(:[]=) Puppet.stubs(:[]).with(:config) Puppet.stubs(:[]).with(:noop).returns(true) Puppet.expects(:[]=).with(:show_diff, true) @main.run_setup end it "should set console as the log destination if logdest option wasn't provided" do Puppet::Log.expects(:newdestination).with(:console) @main.run_setup end it "should set INT trap" do @main.expects(:trap).with(:INT) @main.run_setup end it "should set log level to debug if --debug was passed" do @main.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @main.run_setup end it "should set log level to info if --verbose was passed" do @main.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @main.run_setup end it "should print puppet config if asked to in Puppet config" do @main.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @main.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @main.run_setup }.should raise_error(SystemExit) end end describe "when executing" do it "should dispatch to parseonly if parseonly is set" do @main.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(true) @main.get_command.should == :parseonly end it "should dispatch to 'apply' if it was called with 'apply'" do @main.options[:catalog] = "foo" @main.get_command.should == :apply end it "should dispatch to main if parseonly is not set" do @main.stubs(:options).returns({}) Puppet.stubs(:[]).with(:parseonly).returns(false) @main.get_command.should == :main end describe "the parseonly command" do before :each do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") - @interpreter = stub_everything Puppet.stubs(:err) @main.stubs(:exit) @main.options.stubs(:[]).with(:code).returns "some code" - Puppet::Parser::Interpreter.stubs(:new).returns(@interpreter) + @collection = stub_everything + Puppet::Parser::ResourceTypeCollection.stubs(:new).returns(@collection) end - it "should delegate to the Puppet Parser" do - - @interpreter.expects(:parser) - + it "should use a Puppet Resource Type Collection to parse the file" do + @collection.expects(:perform_initial_import) @main.parseonly end it "should exit with exit code 0 if no error" do @main.expects(:exit).with(0) - @main.parseonly end it "should exit with exit code 1 if error" do - @interpreter.stubs(:parser).raises(Puppet::ParseError) - + @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) @main.expects(:exit).with(1) - @main.parseonly end - end describe "the main command" do before :each do Puppet.stubs(:[]) Puppet.settings.stubs(:use) Puppet.stubs(:[]).with(:prerun_command).returns "" Puppet.stubs(:[]).with(:postrun_command).returns "" Puppet.stubs(:[]).with(:trace).returns(true) @main.options.stubs(:[]) @facts = stub_everything 'facts' Puppet::Node::Facts.stubs(:find).returns(@facts) @node = stub_everything 'node' Puppet::Node.stubs(:find).returns(@node) @catalog = stub_everything 'catalog' @catalog.stubs(:to_ral).returns(@catalog) Puppet::Resource::Catalog.stubs(:find).returns(@catalog) STDIN.stubs(:read) @transaction = stub_everything 'transaction' @catalog.stubs(:apply).returns(@transaction) @main.stubs(:exit) end it "should set the code to run from --code" do @main.options.stubs(:[]).with(:code).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @main.main end it "should set the code to run from STDIN if no arguments" do ARGV.stubs(:length).returns(0) STDIN.stubs(:read).returns("code to run") Puppet.expects(:[]=).with(:code,"code to run") @main.main end it "should set the manifest if some files are passed on command line" do ARGV.stubs(:length).returns(1) ARGV.stubs(:shift).returns("site.pp") Puppet.expects(:[]=).with(:manifest,"site.pp") @main.main end it "should collect the node facts" do Puppet::Node::Facts.expects(:find).returns(@facts) @main.main end it "should raise an error if we can't find the node" do Puppet::Node::Facts.expects(:find).returns(nil) lambda { @main.main }.should raise_error end it "should find the node" do Puppet::Node.expects(:find).returns(@node) @main.main end it "should raise an error if we can't find the node" do Puppet::Node.expects(:find).returns(nil) lambda { @main.main }.should raise_error end it "should merge in our node the loaded facts" do @facts.stubs(:values).returns("values") @node.expects(:merge).with("values") @main.main end it "should load custom classes if loadclasses" do @main.options.stubs(:[]).with(:loadclasses).returns(true) Puppet.stubs(:[]).with(:classfile).returns("/etc/puppet/classes.txt") FileTest.stubs(:exists?).with("/etc/puppet/classes.txt").returns(true) FileTest.stubs(:readable?).with("/etc/puppet/classes.txt").returns(true) File.stubs(:read).with("/etc/puppet/classes.txt").returns("class") @node.expects(:classes=) @main.main end it "should compile the catalog" do Puppet::Resource::Catalog.expects(:find).returns(@catalog) @main.main end it "should transform the catalog to ral" do @catalog.expects(:to_ral).returns(@catalog) @main.main end it "should finalize the catalog" do @catalog.expects(:finalize) @main.main end it "should call the prerun and postrun commands on a Configurer instance" do configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:execute_prerun_command) configurer.expects(:execute_postrun_command) @main.main end it "should apply the catalog" do @catalog.expects(:apply) @main.main end describe "with detailed_exitcodes" do it "should exit with report's computed exit status" do Puppet.stubs(:[]).with(:noop).returns(false) @main.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @main.expects(:exit).with(666) @main.main end it "should always exit with 0 if option is disabled" do Puppet.stubs(:[]).with(:noop).returns(false) @main.options.stubs(:[]).with(:detailed_exitcodes).returns(false) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @main.expects(:exit).with(0) @main.main end it "should always exit with 0 if --noop" do Puppet.stubs(:[]).with(:noop).returns(true) @main.options.stubs(:[]).with(:detailed_exitcodes).returns(true) report = stub 'report', :exit_status => 666 @transaction.stubs(:report).returns(report) @main.expects(:exit).with(0) @main.main end end end describe "the 'apply' command" do it "should read the catalog in from disk if a file name is provided" do @main.options[:catalog] = "/my/catalog.pson" File.expects(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @main.apply end it "should read the catalog in from stdin if '-' is provided" do @main.options[:catalog] = "-" $stdin.expects(:read).returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns Puppet::Resource::Catalog.new @main.apply end it "should deserialize the catalog from the default format" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:default_format).returns :rot13_piglatin Puppet::Resource::Catalog.stubs(:convert_from).with(:rot13_piglatin,'something').returns Puppet::Resource::Catalog.new @main.apply end it "should fail helpfully if deserializing fails" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something syntacically invalid" lambda { @main.apply }.should raise_error(Puppet::Error) end it "should convert plain data structures into a catalog if deserialization does not do so" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,"something").returns({:foo => "bar"}) Puppet::Resource::Catalog.expects(:pson_create).with({:foo => "bar"}).returns(Puppet::Resource::Catalog.new) @main.apply end it "should convert the catalog to a RAL catalog and use a Configurer instance to apply it" do @main.options[:catalog] = "/my/catalog.pson" File.stubs(:read).with("/my/catalog.pson").returns "something" catalog = Puppet::Resource::Catalog.new Puppet::Resource::Catalog.stubs(:convert_from).with(:pson,'something').returns catalog catalog.expects(:to_ral).returns "mycatalog" configurer = stub 'configurer' Puppet::Configurer.expects(:new).returns configurer configurer.expects(:run).with(:catalog => "mycatalog") @main.apply end end end end diff --git a/spec/unit/application/server.rb b/spec/unit/application/server.rb index 52b8d4751..ba0fcfef2 100644 --- a/spec/unit/application/server.rb +++ b/spec/unit/application/server.rb @@ -1,457 +1,450 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' require 'puppet/application/server' describe "PuppetMaster" do before :each do @server_app = Puppet::Application[:server] @daemon = stub_everything 'daemon' Puppet::Daemon.stubs(:new).returns(@daemon) Puppet::Util::Log.stubs(:newdestination) Puppet::Util::Log.stubs(:level=) Puppet::Node.stubs(:terminus_class=) Puppet::Node.stubs(:cache_class=) Puppet::Node::Facts.stubs(:terminus_class=) Puppet::Node::Facts.stubs(:cache_class=) Puppet::Transaction::Report.stubs(:terminus_class=) Puppet::Resource::Catalog.stubs(:terminus_class=) end it "should ask Puppet::Application to parse Puppet configuration file" do @server_app.should_parse_config?.should be_true end it "should declare a main command" do @server_app.should respond_to(:main) end it "should declare a parseonly command" do @server_app.should respond_to(:parseonly) end it "should declare a compile command" do @server_app.should respond_to(:compile) end it "should declare a preinit block" do @server_app.should respond_to(:run_preinit) end describe "during preinit" do before :each do @server_app.stubs(:trap) end it "should catch INT" do @server_app.stubs(:trap).with { |arg,block| arg == :INT } @server_app.run_preinit end it "should create a Puppet Daemon" do Puppet::Daemon.expects(:new).returns(@daemon) @server_app.run_preinit end it "should give ARGV to the Daemon" do argv = stub 'argv' ARGV.stubs(:dup).returns(argv) @daemon.expects(:argv=).with(argv) @server_app.run_preinit end end [:debug,:verbose].each do |option| it "should declare handle_#{option} method" do @server_app.should respond_to("handle_#{option}".to_sym) end it "should store argument value when calling handle_#{option}" do @server_app.options.expects(:[]=).with(option, 'arg') @server_app.send("handle_#{option}".to_sym, 'arg') end end describe "when applying options" do before do @old_argv = ARGV.dup ARGV.clear end after do ARGV.clear @old_argv.each { |a| ARGV << a } end it "should set the log destination with --logdest" do Puppet::Log.expects(:newdestination).with("console") @server_app.handle_logdest("console") end it "should put the setdest options to true" do @server_app.options.expects(:[]=).with(:setdest,true) @server_app.handle_logdest("console") end it "should parse the log destination from ARGV" do ARGV << "--logdest" << "/my/file" Puppet::Util::Log.expects(:newdestination).with("/my/file") @server_app.parse_options end end describe "during setup" do before :each do Puppet::Log.stubs(:newdestination) Puppet.stubs(:settraps) Puppet::Log.stubs(:level=) Puppet::SSL::CertificateAuthority.stubs(:instance) Puppet::SSL::CertificateAuthority.stubs(:ca?) Puppet.settings.stubs(:use) @server_app.options.stubs(:[]).with(any_parameters) end it "should set log level to debug if --debug was passed" do @server_app.options.stubs(:[]).with(:debug).returns(true) Puppet::Log.expects(:level=).with(:debug) @server_app.run_setup end it "should set log level to info if --verbose was passed" do @server_app.options.stubs(:[]).with(:verbose).returns(true) Puppet::Log.expects(:level=).with(:info) @server_app.run_setup end it "should set console as the log destination if no --logdest and --daemonize" do @server_app.stubs(:[]).with(:daemonize).returns(:false) Puppet::Log.expects(:newdestination).with(:syslog) @server_app.run_setup end it "should set syslog as the log destination if no --logdest and not --daemonize" do Puppet::Log.expects(:newdestination).with(:syslog) @server_app.run_setup end it "should set syslog as the log destination if --rack" do @server_app.options.stubs(:[]).with(:rack).returns(:true) Puppet::Log.expects(:newdestination).with(:syslog) @server_app.run_setup end it "should print puppet config if asked to in Puppet config" do @server_app.stubs(:exit) Puppet.settings.stubs(:print_configs?).returns(true) Puppet.settings.expects(:print_configs) @server_app.run_setup end it "should exit after printing puppet config if asked to in Puppet config" do Puppet.settings.stubs(:print_configs?).returns(true) lambda { @server_app.run_setup }.should raise_error(SystemExit) end it "should tell Puppet.settings to use :main,:ssl and :server_app category" do Puppet.settings.expects(:use).with(:main,:puppetmasterd,:ssl) @server_app.run_setup end it "should set node facst terminus to yaml" do Puppet::Node::Facts.expects(:terminus_class=).with(:yaml) @server_app.run_setup end it "should cache class in yaml" do Puppet::Node.expects(:cache_class=).with(:yaml) @server_app.run_setup end describe "with no ca" do it "should set the ca_location to none" do Puppet::SSL::Host.expects(:ca_location=).with(:none) @server_app.run_setup end end describe "with a ca configured" do before :each do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) end it "should set the ca_location to local" do Puppet::SSL::Host.expects(:ca_location=).with(:local) @server_app.run_setup end it "should tell Puppet.settings to use :ca category" do Puppet.settings.expects(:use).with(:ca) @server_app.run_setup end it "should instantiate the CertificateAuthority singleton" do Puppet::SSL::CertificateAuthority.expects(:instance) @server_app.run_setup end end end describe "when running" do it "should dispatch to parseonly if parseonly is set" do Puppet.stubs(:[]).with(:parseonly).returns(true) @server_app.options[:node] = nil @server_app.get_command.should == :parseonly end it "should dispatch to compile if called with --compile" do @server_app.options[:node] = "foo" @server_app.get_command.should == :compile end it "should dispatch to main if parseonly is not set" do Puppet.stubs(:[]).with(:parseonly).returns(false) @server_app.options[:node] = nil @server_app.get_command.should == :main end + describe "the parseonly command" do before :each do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") - @interpreter = stub_everything Puppet.stubs(:err) @server_app.stubs(:exit) - Puppet::Parser::Interpreter.stubs(:new).returns(@interpreter) + @collection = stub_everything + Puppet::Parser::ResourceTypeCollection.stubs(:new).returns(@collection) end - it "should delegate to the Puppet Parser" do - - @interpreter.expects(:parser) - + it "should use a Puppet Resource Type Collection to parse the file" do + @collection.expects(:perform_initial_import) @server_app.parseonly end it "should exit with exit code 0 if no error" do @server_app.expects(:exit).with(0) - @server_app.parseonly end it "should exit with exit code 1 if error" do - @interpreter.stubs(:parser).raises(Puppet::ParseError) - + @collection.stubs(:perform_initial_import).raises(Puppet::ParseError) @server_app.expects(:exit).with(1) - @server_app.parseonly end - end describe "the compile command" do before do Puppet.stubs(:[]).with(:environment) Puppet.stubs(:[]).with(:manifest).returns("site.pp") - @interpreter = stub_everything Puppet.stubs(:err) @server_app.stubs(:exit) - Puppet::Parser::Interpreter.stubs(:new).returns(@interpreter) Puppet.features.stubs(:pson?).returns true end it "should fail if pson isn't available" do Puppet.features.expects(:pson?).returns false lambda { @server_app.compile }.should raise_error end it "should compile a catalog for the specified node" do @server_app.options[:node] = "foo" Puppet::Resource::Catalog.expects(:find).with("foo").returns Puppet::Resource::Catalog.new $stdout.stubs(:puts) @server_app.compile end it "should render the catalog to pson and print the output" do @server_app.options[:node] = "foo" catalog = Puppet::Resource::Catalog.new catalog.expects(:render).with(:pson).returns "mypson" Puppet::Resource::Catalog.expects(:find).returns catalog $stdout.expects(:puts).with("mypson") @server_app.compile end it "should exit with error code 30 if no catalog can be found" do @server_app.options[:node] = "foo" Puppet::Resource::Catalog.expects(:find).returns nil @server_app.expects(:exit).with(30) $stderr.expects(:puts) @server_app.compile end it "should exit with error code 30 if there's a failure" do @server_app.options[:node] = "foo" Puppet::Resource::Catalog.expects(:find).raises ArgumentError @server_app.expects(:exit).with(30) $stderr.expects(:puts) @server_app.compile end end describe "the main command" do before :each do @server_app.run_preinit @server = stub_everything 'server' Puppet::Network::Server.stubs(:new).returns(@server) @app = stub_everything 'app' Puppet::SSL::Host.stubs(:localhost) Puppet::SSL::CertificateAuthority.stubs(:ca?) Process.stubs(:uid).returns(1000) Puppet.stubs(:service) Puppet.stubs(:[]) Puppet.stubs(:notice) Puppet.stubs(:start) end it "should create a Server" do Puppet::Network::Server.expects(:new) @server_app.main end it "should give the server to the daemon" do @daemon.expects(:server=).with(@server) @server_app.main end it "should create the server with the right XMLRPC handlers" do Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket]} @server_app.main end it "should create the server with a :ca xmlrpc handler if needed" do Puppet.stubs(:[]).with(:ca).returns(true) Puppet::Network::Server.expects(:new).with { |args| args[:xmlrpc_handlers].include?(:CA) } @server_app.main end it "should generate a SSL cert for localhost" do Puppet::SSL::Host.expects(:localhost) @server_app.main end it "should make sure to *only* hit the CA for data" do Puppet::SSL::CertificateAuthority.stubs(:ca?).returns(true) Puppet::SSL::Host.expects(:ca_location=).with(:only) @server_app.main end it "should drop privileges if running as root" do Process.stubs(:uid).returns(0) Puppet::Util.expects(:chuser) @server_app.main end it "should daemonize if needed" do Puppet.stubs(:[]).with(:daemonize).returns(true) @daemon.expects(:daemonize) @server_app.main end it "should start the service" do @daemon.expects(:start) @server_app.main end describe "with --rack" do confine "Rack is not available" => Puppet.features.rack? before do require 'puppet/network/http/rack' Puppet::Network::HTTP::Rack.stubs(:new).returns(@app) end it "it should create the app with REST and XMLRPC support" do @server_app.options.stubs(:[]).with(:rack).returns(:true) Puppet::Network::HTTP::Rack.expects(:new).with { |args| args[:xmlrpc_handlers] == [:Status, :FileServer, :Master, :Report, :Filebucket] and args[:protocols] == [:rest, :xmlrpc] } @server_app.main end it "it should not start a daemon" do @server_app.options.stubs(:[]).with(:rack).returns(:true) @daemon.expects(:start).never @server_app.main end it "it should return the app" do @server_app.options.stubs(:[]).with(:rack).returns(:true) app = @server_app.main app.should equal(@app) end end end end end diff --git a/spec/unit/indirector/catalog/compiler.rb b/spec/unit/indirector/catalog/compiler.rb index e2c18de57..d11daaa96 100755 --- a/spec/unit/indirector/catalog/compiler.rb +++ b/spec/unit/indirector/catalog/compiler.rb @@ -1,288 +1,265 @@ #!/usr/bin/env ruby # # Created by Luke Kanies on 2007-9-23. # Copyright (c) 2007. All rights reserved. require File.dirname(__FILE__) + '/../../../spec_helper' require 'puppet/indirector/catalog/compiler' describe Puppet::Resource::Catalog::Compiler do before do Puppet::Rails.stubs(:init) Facter.stubs(:to_hash).returns({}) Facter.stubs(:[]).returns(Facter::Util::Fact.new("something")) end describe "when initializing" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") end it "should gather data about itself" do Puppet::Resource::Catalog::Compiler.new end it "should cache the server metadata and reuse it" do compiler = Puppet::Resource::Catalog::Compiler.new node1 = stub 'node1', :merge => nil node2 = stub 'node2', :merge => nil compiler.stubs(:compile) Puppet::Node.stubs(:find).with('node1').returns(node1) Puppet::Node.stubs(:find).with('node2').returns(node2) compiler.find(stub('request', :node => 'node1', :options => {})) compiler.find(stub('node2request', :node => 'node2', :options => {})) end it "should provide a method for determining if the catalog is networked" do compiler = Puppet::Resource::Catalog::Compiler.new compiler.should respond_to(:networked?) end describe "and storeconfigs is enabled" do before do Puppet.settings[:storeconfigs] = true end it "should initialize Rails if it is available" do Puppet.features.expects(:rails?).returns true Puppet::Rails.expects(:init) Puppet::Resource::Catalog::Compiler.new end it "should fail if Rails is unavailable" do Puppet.features.expects(:rails?).returns false Puppet::Rails.expects(:init).never lambda { Puppet::Resource::Catalog::Compiler.new }.should raise_error(Puppet::Error) end end end - describe "when creating the interpreter" do - before do - # This gets pretty annoying on a plane where we have no IP address - Facter.stubs(:value).returns("whatever") - @compiler = Puppet::Resource::Catalog::Compiler.new - end - - it "should not create the interpreter until it is asked for the first time" do - interp = mock 'interp' - Puppet::Parser::Interpreter.expects(:new).with().returns(interp) - @compiler.interpreter.should equal(interp) - end - - it "should use the same interpreter for all compiles" do - interp = mock 'interp' - Puppet::Parser::Interpreter.expects(:new).with().returns(interp) - @compiler.interpreter.should equal(interp) - @compiler.interpreter.should equal(interp) - end - end - describe "when finding catalogs" do before do Facter.stubs(:value).returns("whatever") - env = stub 'environment', :name => "yay", :modulepath => [] - Puppet::Node::Environment.stubs(:new).returns(env) @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = Puppet::Node.new @name @node.stubs(:merge) Puppet::Node.stubs(:find).returns @node @request = stub 'request', :key => "does not matter", :node => @name, :options => {} end it "should directly use provided nodes" do Puppet::Node.expects(:find).never @compiler.expects(:compile).with(@node) @request.stubs(:options).returns(:use_node => @node) @compiler.find(@request) end it "should use the request's node name if no explicit node is provided" do Puppet::Node.expects(:find).with(@name).returns(@node) @compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should use the provided node name if no explicit node is provided and no authenticated node information is available" do @request.expects(:node).returns nil @request.expects(:key).returns "my_node" Puppet::Node.expects(:find).with("my_node").returns @node @compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should fail if no node is passed and none can be found" do Puppet::Node.stubs(:find).with(@name).returns(nil) proc { @compiler.find(@request) }.should raise_error(ArgumentError) end it "should fail intelligently when searching for a node raises an exception" do Puppet::Node.stubs(:find).with(@name).raises "eh" proc { @compiler.find(@request) }.should raise_error(Puppet::Error) end - it "should pass the found node to the interpreter for compiling" do + it "should pass the found node to the compiler for compiling" do Puppet::Node.expects(:find).with(@name).returns(@node) config = mock 'config' - @compiler.interpreter.expects(:compile).with(@node) + Puppet::Parser::Compiler.expects(:compile).with(@node) @compiler.find(@request) end it "should extract and save any facts from the request" do Puppet::Node.expects(:find).with(@name).returns @node @compiler.expects(:extract_facts_from_request).with(@request) - @compiler.interpreter.stubs(:compile) + Puppet::Parser::Compiler.stubs(:compile) @compiler.find(@request) end it "should return the results of compiling as the catalog" do Puppet::Node.stubs(:find).returns(@node) config = mock 'config' result = mock 'result' - @compiler.interpreter.expects(:compile).with(@node).returns(result) + Puppet::Parser::Compiler.expects(:compile).returns result @compiler.find(@request).should equal(result) end it "should benchmark the compile process" do Puppet::Node.stubs(:find).returns(@node) @compiler.stubs(:networked?).returns(true) @compiler.expects(:benchmark).with do |level, message| level == :notice and message =~ /^Compiled catalog/ end - @compiler.interpreter.stubs(:compile).with(@node) + Puppet::Parser::Compiler.stubs(:compile) @compiler.find(@request) end end describe "when extracting facts from the request" do before do @compiler = Puppet::Resource::Catalog::Compiler.new @request = stub 'request', :options => {} @facts = stub 'facts', :save => nil Facter.stubs(:value).returns "something" end it "should do nothing if no facts are provided" do Puppet::Node::Facts.expects(:convert_from).never @request.options[:facts] = nil @compiler.extract_facts_from_request(@request) end it "should use the Facts class to deserialize the provided facts" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts @compiler.extract_facts_from_request(@request) end it "should use the provided fact format" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).with { |format, text| format == "foo" }.returns @facts @compiler.extract_facts_from_request(@request) end it "should convert the facts into a fact instance and save it" do @request.options[:facts_format] = "foo" @request.options[:facts] = "bar" Puppet::Node::Facts.expects(:convert_from).returns @facts @facts.expects(:save) @compiler.extract_facts_from_request(@request) end end describe "when finding nodes" do before do Facter.stubs(:value).returns("whatever") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = stub 'request', :node => @name, :options => {} @compiler.stubs(:compile) end it "should look node information up via the Node class with the provided key" do @node.stubs :merge Puppet::Node.expects(:find).with(@name).returns(@node) @compiler.find(@request) end end describe "after finding nodes" do before do Puppet.expects(:version).returns(1) Facter.expects(:value).with('fqdn').returns("my.server.com") Facter.expects(:value).with('ipaddress').returns("my.ip.address") @compiler = Puppet::Resource::Catalog::Compiler.new @name = "me" @node = mock 'node' @request = stub 'request', :node => @name, :options => {} @compiler.stubs(:compile) Puppet::Node.stubs(:find).with(@name).returns(@node) end it "should add the server's Puppet version to the node's parameters as 'serverversion'" do @node.expects(:merge).with { |args| args["serverversion"] == "1" } @compiler.find(@request) end it "should add the server's fqdn to the node's parameters as 'servername'" do @node.expects(:merge).with { |args| args["servername"] == "my.server.com" } @compiler.find(@request) end it "should add the server's IP address to the node's parameters as 'serverip'" do @node.expects(:merge).with { |args| args["serverip"] == "my.ip.address" } @compiler.find(@request) end end describe "when filtering resources" do before :each do Facter.stubs(:value) @compiler = Puppet::Resource::Catalog::Compiler.new @catalog = stub_everything 'catalog' @catalog.stubs(:respond_to?).with(:filter).returns(true) end it "should delegate to the catalog instance filtering" do @catalog.expects(:filter) @compiler.filter(@catalog) end it "should filter out virtual resources" do resource = mock 'resource', :virtual? => true @catalog.stubs(:filter).yields(resource) @compiler.filter(@catalog) end it "should return the same catalog if it doesn't support filtering" do @catalog.stubs(:respond_to?).with(:filter).returns(false) @compiler.filter(@catalog).should == @catalog end it "should return the filtered catalog" do catalog = stub 'filtered catalog' @catalog.stubs(:filter).returns(catalog) @compiler.filter(@catalog).should == catalog end end end diff --git a/spec/unit/parser/compiler.rb b/spec/unit/parser/compiler.rb index cf73f4d3b..4646f77a3 100755 --- a/spec/unit/parser/compiler.rb +++ b/spec/unit/parser/compiler.rb @@ -1,589 +1,605 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' class CompilerTestResource attr_accessor :builtin, :virtual, :evaluated, :type, :title def initialize(type, title) @type = type @title = title end def ref "%s[%s]" % [type.to_s.capitalize, title] end def evaluated? @evaluated end def builtin? @builtin end def virtual? @virtual end def evaluate end end describe Puppet::Parser::Compiler do before :each do @node = Puppet::Node.new "testnode" @known_resource_types = Puppet::Parser::ResourceTypeCollection.new "development" @scope_resource = stub 'scope_resource', :builtin? => true, :finish => nil, :ref => 'Class[main]', :type => "class" @scope = stub 'scope', :resource => @scope_resource, :source => mock("source") @compiler = Puppet::Parser::Compiler.new(@node) @compiler.environment.stubs(:known_resource_types).returns @known_resource_types end + it "should have a class method that compiles, converts, and returns a catalog" do + compiler = stub 'compiler' + Puppet::Parser::Compiler.expects(:new).with(@node).returns compiler + catalog = stub 'catalog' + compiler.expects(:compile).returns catalog + converted_catalog = stub 'converted_catalog' + catalog.expects(:to_resource).returns converted_catalog + + Puppet::Parser::Compiler.compile(@node).should equal(converted_catalog) + end + + it "should fail intelligently when a class-level compile fails" do + Puppet::Parser::Compiler.expects(:new).raises ArgumentError + lambda { Puppet::Parser::Compiler.compile(@node) }.should raise_error(Puppet::Error) + end + it "should use the node's environment as its environment" do @compiler.environment.should equal(@node.environment) end it "should include the resource type collection helper" do Puppet::Parser::Compiler.ancestors.should be_include(Puppet::Parser::ResourceTypeCollectionHelper) end it "should be able to return a class list containing all added classes" do @compiler.add_class "" @compiler.add_class "one" @compiler.add_class "two" @compiler.classlist.sort.should == %w{one two}.sort end describe "when initializing" do it "should set its node attribute" do @compiler.node.should equal(@node) end it "should detect when ast nodes are absent" do @compiler.ast_nodes?.should be_false end it "should detect when ast nodes are present" do @known_resource_types.expects(:nodes?).returns true @compiler.ast_nodes?.should be_true end it "should copy the known_resource_types version to the catalog" do @compiler.catalog.version.should == @known_resource_types.version end it "should copy any node classes into the class list" do node = Puppet::Node.new("mynode") node.classes = %w{foo bar} compiler = Puppet::Parser::Compiler.new(node) compiler.classlist.should include("foo") compiler.classlist.should include("bar") end end describe "when managing scopes" do it "should create a top scope" do @compiler.topscope.should be_instance_of(Puppet::Parser::Scope) end it "should be able to create new scopes" do @compiler.newscope(@compiler.topscope).should be_instance_of(Puppet::Parser::Scope) end it "should correctly set the level of newly created scopes" do @compiler.newscope(@compiler.topscope, :level => 5).level.should == 5 end it "should set the parent scope of the new scope to be the passed-in parent" do scope = mock 'scope' newscope = @compiler.newscope(scope) newscope.parent.should equal(scope) end it "should set the parent scope of the new scope to its topscope if the parent passed in is nil" do scope = mock 'scope' newscope = @compiler.newscope(nil) newscope.parent.should equal(@compiler.topscope) end end describe "when compiling" do def compile_methods [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, :finish, :store, :extract] end # Stub all of the main compile methods except the ones we're specifically interested in. def compile_stub(*except) (compile_methods - except).each { |m| @compiler.stubs(m) } end it "should set node parameters as variables in the top scope" do params = {"a" => "b", "c" => "d"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.topscope.lookupvar("a").should == "b" @compiler.topscope.lookupvar("c").should == "d" end it "should set the client and server versions on the catalog" do params = {"clientversion" => "2", "serverversion" => "3"} @node.stubs(:parameters).returns(params) compile_stub(:set_node_parameters) @compiler.compile @compiler.catalog.client_version.should == "2" @compiler.catalog.server_version.should == "3" end it "should evaluate any existing classes named in the node" do classes = %w{one two three four} main = stub 'main' one = stub 'one', :name => "one" three = stub 'three', :name => "three" @node.stubs(:name).returns("whatever") @node.stubs(:classes).returns(classes) @compiler.expects(:evaluate_classes).with(classes, @compiler.topscope) @compiler.class.publicize_methods(:evaluate_node_classes) { @compiler.evaluate_node_classes } end it "should evaluate the main class if it exists" do compile_stub(:evaluate_main) main_class = mock 'main_class' main_class.expects(:evaluate_code).with { |r| r.is_a?(Puppet::Parser::Resource) } @compiler.topscope.expects(:source=).with(main_class) @known_resource_types.stubs(:find_hostclass).with("", "").returns(main_class) @compiler.compile end it "should create a new, empty 'main' if no main class exists" do compile_stub(:evaluate_main) @compiler.compile @known_resource_types.find_hostclass("", "").should be_instance_of(Puppet::Parser::ResourceType) end it "should evaluate any node classes" do @node.stubs(:classes).returns(%w{one two three four}) @compiler.expects(:evaluate_classes).with(%w{one two three four}, @compiler.topscope) @compiler.send(:evaluate_node_classes) end it "should evaluate all added collections" do colls = [] # And when the collections fail to evaluate. colls << mock("coll1-false") colls << mock("coll2-false") colls.each { |c| c.expects(:evaluate).returns(false) } @compiler.add_collection(colls[0]) @compiler.add_collection(colls[1]) compile_stub(:evaluate_generators) @compiler.compile end it "should ignore builtin resources" do resource = stub 'builtin', :ref => "File[testing]", :builtin? => true, :type => "file" @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true } @compiler.compile end it "should not evaluate already-evaluated resources" do resource = stub 'already_evaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => true, :virtual? => false, :type => "file" @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end it "should evaluate unevaluated resources created by evaluating other resources" do resource = CompilerTestResource.new(:file, "testing") @compiler.add_resource(@scope, resource) resource2 = CompilerTestResource.new(:file, "other") # We have to now mark the resource as evaluated resource.expects(:evaluate).with { |*whatever| resource.evaluated = true; @compiler.add_resource(@scope, resource2) } resource2.expects(:evaluate).with { |*whatever| resource2.evaluated = true } @compiler.compile end it "should call finish() on all resources" do # Add a resource that does respond to :finish resource = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish" resource.expects(:finish) @compiler.add_resource(@scope, resource) # And one that does not dnf = stub "dnf", :ref => "File[dnf]", :type => "file" @compiler.add_resource(@scope, dnf) @compiler.send(:finish) end it "should call finish() in add_resource order" do resources = sequence('resources') resource1 = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish1" resource1.expects(:finish).in_sequence(resources) @compiler.add_resource(@scope, resource1) resource2 = Puppet::Parser::Resource.new :scope => @scope, :type => "file", :title => "finish2" resource2.expects(:finish).in_sequence(resources) @compiler.add_resource(@scope, resource2) @compiler.send(:finish) end it "should return added resources in add order" do resource1 = stub "1", :ref => "File[yay]", :type => "file" @compiler.add_resource(@scope, resource1) resource2 = stub "2", :ref => "File[youpi]", :type => "file" @compiler.add_resource(@scope, resource2) @compiler.resources.should == [resource1, resource2] end it "should add resources that do not conflict with existing resources" do resource = CompilerTestResource.new(:file, "yay") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_vertex(resource) end it "should fail to add resources that conflict with existing resources" do file1 = Puppet::Type.type(:file).new :path => "/foo" file2 = Puppet::Type.type(:file).new :path => "/foo" @compiler.add_resource(@scope, file1) lambda { @compiler.add_resource(@scope, file2) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should add an edge from the scope resource to the added resource" do resource = stub "noconflict", :ref => "File[yay]", :type => "file" @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should add edges from the class resources to the main class" do main = CompilerTestResource.new(:class, :main) @compiler.add_resource(@scope, main) resource = CompilerTestResource.new(:class, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(main, resource) end it "should just add edges to the scope resource for the class resources when no main class can be found" do resource = CompilerTestResource.new(:class, "foo") @compiler.add_resource(@scope, resource) @compiler.catalog.should be_edge(@scope.resource, resource) end it "should have a method for looking up resources" do resource = stub 'resource', :ref => "Yay[foo]", :type => "file" @compiler.add_resource(@scope, resource) @compiler.findresource("Yay[foo]").should equal(resource) end it "should be able to look resources up by type and title" do resource = stub 'resource', :ref => "Yay[foo]", :type => "file" @compiler.add_resource(@scope, resource) @compiler.findresource("Yay", "foo").should equal(resource) end it "should not evaluate virtual defined resources" do resource = stub 'notevaluated', :ref => "File[testing]", :builtin? => false, :evaluated? => false, :virtual? => true, :type => "file" @compiler.add_resource(@scope, resource) resource.expects(:evaluate).never @compiler.compile end end describe "when evaluating collections" do it "should evaluate each collection" do 2.times { |i| coll = mock 'coll%s' % i @compiler.add_collection(coll) # This is the hard part -- we have to emulate the fact that # collections delete themselves if they are done evaluating. coll.expects(:evaluate).with do @compiler.delete_collection(coll) end } @compiler.class.publicize_methods(:evaluate_collections) { @compiler.evaluate_collections } end it "should not fail when there are unevaluated resource collections that do not refer to specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(nil) @compiler.add_collection(coll) lambda { @compiler.compile }.should_not raise_error end it "should fail when there are unevaluated resource collections that refer to a specific resource" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns(:something) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error(Puppet::ParseError) end it "should fail when there are unevaluated resource collections that refer to multiple specific resources" do coll = stub 'coll', :evaluate => false coll.expects(:resources).returns([:one, :two]) @compiler.add_collection(coll) lambda { @compiler.compile }.should raise_error(Puppet::ParseError) end end describe "when told to evaluate missing classes" do it "should fail if there's no source listed for the scope" do scope = stub 'scope', :source => nil proc { @compiler.evaluate_classes(%w{one two}, scope) }.should raise_error(Puppet::DevError) end it "should tag the catalog with the name of each not-found class" do @compiler.catalog.expects(:tag).with("notfound") @scope.expects(:find_hostclass).with("notfound").returns(nil) @compiler.evaluate_classes(%w{notfound}, @scope) end end describe "when evaluating found classes" do before do @class = stub 'class', :name => "my::class" @scope.stubs(:find_hostclass).with("myclass").returns(@class) @resource = stub 'resource', :ref => "Class[myclass]", :type => "file" end it "should evaluate each class" do @compiler.catalog.stubs(:tag) @class.expects(:mk_plain_resource).with(@scope) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should not evaluate the resources created for found classes unless asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate).never @class.expects(:mk_plain_resource).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope) end it "should immediately evaluate the resources created for found classes when asked" do @compiler.catalog.stubs(:tag) @resource.expects(:evaluate) @class.expects(:mk_plain_resource).returns(@resource) @scope.stubs(:class_scope).with(@class) @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes that have already been evaluated" do @compiler.catalog.stubs(:tag) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{myclass}, @scope, false) end it "should skip classes previously evaluated with different capitalization" do @compiler.catalog.stubs(:tag) @scope.stubs(:find_hostclass).with("MyClass").returns(@class) @scope.stubs(:class_scope).with(@class).returns("something") @compiler.expects(:add_resource).never @resource.expects(:evaluate).never Puppet::Parser::Resource.expects(:new).never @compiler.evaluate_classes(%w{MyClass}, @scope, false) end it "should return the list of found classes" do @compiler.catalog.stubs(:tag) @compiler.stubs(:add_resource) @scope.stubs(:find_hostclass).with("notfound").returns(nil) @scope.stubs(:class_scope).with(@class) Puppet::Parser::Resource.stubs(:new).returns(@resource) @class.stubs :mk_plain_resource @compiler.evaluate_classes(%w{myclass notfound}, @scope).should == %w{myclass} end end describe "when evaluating AST nodes with no AST nodes present" do it "should do nothing" do @compiler.expects(:ast_nodes?).returns(false) @compiler.known_resource_types.expects(:nodes).never Puppet::Parser::Resource.expects(:new).never @compiler.send(:evaluate_ast_node) end end describe "when evaluating AST nodes with AST nodes present" do before do @compiler.known_resource_types.stubs(:nodes?).returns true # Set some names for our test @node.stubs(:names).returns(%w{a b c}) @compiler.known_resource_types.stubs(:node).with("a").returns(nil) @compiler.known_resource_types.stubs(:node).with("b").returns(nil) @compiler.known_resource_types.stubs(:node).with("c").returns(nil) # It should check this last, of course. @compiler.known_resource_types.stubs(:node).with("default").returns(nil) end it "should fail if the named node cannot be found" do proc { @compiler.send(:evaluate_ast_node) }.should raise_error(Puppet::ParseError) end it "should evaluate the first node class matching the node name" do node_class = stub 'node', :name => "c", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class.expects(:mk_plain_resource).returns(node_resource) @compiler.compile end it "should match the default node if no matching node can be found" do node_class = stub 'node', :name => "default", :evaluate_code => nil @compiler.known_resource_types.stubs(:node).with("default").returns(node_class) node_resource = stub 'node resource', :ref => "Node[default]", :evaluate => nil, :type => "node" node_class.expects(:mk_plain_resource).returns(node_resource) @compiler.compile end it "should evaluate the node resource immediately rather than using lazy evaluation" do node_class = stub 'node', :name => "c" @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) node_resource = stub 'node resource', :ref => "Node[c]", :type => "node" node_class.expects(:mk_plain_resource).returns(node_resource) node_resource.expects(:evaluate) @compiler.send(:evaluate_ast_node) end it "should set the node's scope as the top scope" do node_resource = stub 'node resource', :ref => "Node[c]", :evaluate => nil, :type => "node" node_class = stub 'node', :name => "c", :mk_plain_resource => node_resource @compiler.known_resource_types.stubs(:node).with("c").returns(node_class) # The #evaluate method normally does this. scope = stub 'scope', :source => "mysource" @compiler.topscope.expects(:class_scope).with(node_class).returns(scope) node_resource.stubs(:evaluate) @compiler.compile @compiler.topscope.should equal(scope) end end describe "when managing resource overrides" do before do @override = stub 'override', :ref => "My[ref]", :type => "my" @resource = stub 'resource', :ref => "My[ref]", :builtin? => true, :type => "my" end it "should be able to store overrides" do lambda { @compiler.add_override(@override) }.should_not raise_error end it "should apply overrides to the appropriate resources" do @compiler.add_resource(@scope, @resource) @resource.expects(:merge).with(@override) @compiler.add_override(@override) @compiler.compile end it "should accept overrides before the related resource has been created" do @resource.expects(:merge).with(@override) # First store the override @compiler.add_override(@override) # Then the resource @compiler.add_resource(@scope, @resource) # And compile, so they get resolved @compiler.compile end it "should fail if the compile is finished and resource overrides have not been applied" do @compiler.add_override(@override) lambda { @compiler.compile }.should raise_error(Puppet::ParseError) end end end diff --git a/spec/unit/parser/interpreter.rb b/spec/unit/parser/interpreter.rb deleted file mode 100755 index 99a0a4be8..000000000 --- a/spec/unit/parser/interpreter.rb +++ /dev/null @@ -1,128 +0,0 @@ -#!/usr/bin/env ruby - -require File.dirname(__FILE__) + '/../../spec_helper' - -describe Puppet::Parser::Interpreter do - before do - @interp = Puppet::Parser::Interpreter.new - @parser = mock 'parser' - end - - describe "when creating parser instances" do - it "should create a parser with code if there is code defined in the :code setting" do - Puppet.settings.stubs(:uninterpolated_value).with(:code, :myenv).returns("mycode") - @parser.expects(:string=).with("mycode") - @parser.expects(:parse) - Puppet::Parser::Parser.expects(:new).with(:myenv).returns(@parser) - @interp.send(:create_parser, :myenv).object_id.should equal(@parser.object_id) - end - - it "should create a parser with the main manifest when the code setting is an empty string" do - Puppet.settings.stubs(:uninterpolated_value).with(:code, :myenv).returns("") - Puppet.settings.stubs(:value).with(:manifest, :myenv).returns("/my/file") - @parser.expects(:parse) - @parser.expects(:file=).with("/my/file") - Puppet::Parser::Parser.expects(:new).with(:myenv).returns(@parser) - @interp.send(:create_parser, :myenv).should equal(@parser) - end - - it "should return nothing when new parsers fail" do - Puppet::Parser::Parser.expects(:new).with(:myenv).raises(ArgumentError) - proc { @interp.send(:create_parser, :myenv) }.should raise_error(Puppet::Error) - end - - it "should create parsers with environment-appropriate manifests" do - # Set our per-environment values. We can't just stub :value, because - # it's called by too much of the rest of the code. - text = "[env1]\nmanifest = /t/env1.pp\n[env2]\nmanifest = /t/env2.pp" - FileTest.stubs(:exist?).returns true - Puppet.settings.stubs(:read_file).returns(text) - Puppet.settings.parse - - parser1 = mock 'parser1' - Puppet::Parser::Parser.expects(:new).with(:env1).returns(parser1) - parser1.expects(:file=).with("/t/env1.pp") - parser1.expects(:parse) - @interp.send(:create_parser, :env1) - - parser2 = mock 'parser2' - Puppet::Parser::Parser.expects(:new).with(:env2).returns(parser2) - parser2.expects(:file=).with("/t/env2.pp") - parser2.expects(:parse) - @interp.send(:create_parser, :env2) - end - end - - describe "when managing parser instances" do - it "should use the same parser when the parser does not need reparsing" do - @interp.expects(:create_parser).with(:myenv).returns(@parser) - @interp.send(:parser, :myenv).should equal(@parser) - - @parser.expects(:reparse?).returns(false) - @interp.send(:parser, :myenv).should equal(@parser) - end - - it "should fail intelligently if a parser cannot be created and one does not already exist" do - @interp.expects(:create_parser).with(:myenv).raises(ArgumentError) - proc { @interp.send(:parser, :myenv) }.should raise_error(ArgumentError) - end - - it "should use different parsers for different environments" do - # get one for the first env - @interp.expects(:create_parser).with(:first_env).returns(@parser) - @interp.send(:parser, :first_env).should equal(@parser) - - other_parser = mock('otherparser') - @interp.expects(:create_parser).with(:second_env).returns(other_parser) - @interp.send(:parser, :second_env).should equal(other_parser) - end - - describe "when files need reparsing" do - it "should create a new parser" do - oldparser = mock('oldparser') - newparser = mock('newparser') - oldparser.expects(:reparse?).returns(true) - - @interp.expects(:create_parser).with(:myenv).returns(oldparser) - @interp.send(:parser, :myenv).should equal(oldparser) - @interp.expects(:create_parser).with(:myenv).returns(newparser) - @interp.send(:parser, :myenv).should equal(newparser) - end - - it "should raise an exception if a new parser cannot be created" do - # Get the first parser in the hash. - @interp.expects(:create_parser).with(:myenv).returns(@parser) - @interp.send(:parser, :myenv).should equal(@parser) - - @parser.expects(:reparse?).returns(true) - - @interp.expects(:create_parser).with(:myenv).raises(Puppet::Error, "Could not parse") - - lambda { @interp.parser(:myenv) }.should raise_error(Puppet::Error) - end - end - end - - describe "when compiling a catalog" do - before do - @node = Puppet::Node.new("foo") - @compiler = mock 'compile' - end - - it "should create a compile with the node" do - catalog = stub 'catalog', :to_resource => nil - @compiler.expects(:compile).returns(catalog) - Puppet::Parser::Compiler.expects(:new).with(@node).returns(@compiler) - @interp.compile(@node) - end - - it "should return the results of the compile, converted to a plain resource catalog" do - catalog = mock 'catalog' - @compiler.expects(:compile).returns(catalog) - Puppet::Parser::Compiler.stubs(:new).returns(@compiler) - - catalog.expects(:to_resource).returns "my_resource_catalog" - @interp.compile(@node).should == "my_resource_catalog" - end - end -end diff --git a/spec/unit/resource/catalog.rb b/spec/unit/resource/catalog.rb index 7f3049136..e2fe72489 100755 --- a/spec/unit/resource/catalog.rb +++ b/spec/unit/resource/catalog.rb @@ -1,1064 +1,1063 @@ #!/usr/bin/env ruby require File.dirname(__FILE__) + '/../../spec_helper' describe Puppet::Resource::Catalog, "when compiling" do it "should be an Expirer" do Puppet::Resource::Catalog.ancestors.should be_include(Puppet::Util::Cacher::Expirer) end it "should always be expired if it's not applying" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.expects(:applying?).returns false @catalog.should be_dependent_data_expired(Time.now) end it "should not be expired if it's applying and the timestamp is late enough" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.expire @catalog.expects(:applying?).returns true @catalog.should_not be_dependent_data_expired(Time.now) end it "should be able to write its list of classes to the class file" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.add_class "foo", "bar" Puppet.settings.expects(:value).with(:classfile).returns "/class/file" fh = mock 'filehandle' File.expects(:open).with("/class/file", "w").yields fh fh.expects(:puts).with "foo\nbar" @catalog.write_class_file end it "should have a client_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.client_version = 5 @catalog.client_version.should == 5 end it "should have a server_version attribute" do @catalog = Puppet::Resource::Catalog.new("host") @catalog.server_version = 5 @catalog.server_version.should == 5 end describe "when compiling" do it "should accept tags" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one") config.tags.should == %w{one} end it "should accept multiple tags at once" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", "two") config.tags.should == %w{one two} end it "should convert all tags to strings" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one", :two) config.tags.should == %w{one two} end it "should tag with both the qualified name and the split name" do config = Puppet::Resource::Catalog.new("mynode") config.tag("one::two") config.tags.include?("one").should be_true config.tags.include?("one::two").should be_true end it "should accept classes" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.classes.should == %w{one} config.add_class("two", "three") config.classes.should == %w{one two three} end it "should tag itself with passed class names" do config = Puppet::Resource::Catalog.new("mynode") config.add_class("one") config.tags.should == %w{one} end end describe "when extracting" do it "should return extraction result as the method result" do config = Puppet::Resource::Catalog.new("mynode") config.expects(:extraction_format).returns(:whatever) config.expects(:extract_to_whatever).returns(:result) config.extract.should == :result end end describe "when extracting transobjects" do def mkscope - @parser = Puppet::Parser::Parser.new :Code => "" @node = Puppet::Node.new("mynode") - @compiler = Puppet::Parser::Compiler.new(@node, @parser) + @compiler = Puppet::Parser::Compiler.new(@node) # XXX This is ridiculous. @compiler.send(:evaluate_main) @scope = @compiler.topscope end def mkresource(type, name) Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope) end it "should always create a TransBucket for the 'main' class" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @source = mock 'source' main = mkresource("class", :main) config.add_vertex(main) bucket = stub 'bucket', :file= => nil, :line= => nil, :classes= => nil bucket.expects(:type=).with("Class") bucket.expects(:name=).with(:main) main.stubs(:builtin?).returns(false) Puppet::TransBucket.expects(:new).returns bucket config.extract_to_transportable.should equal(bucket) end # Now try it with a more complicated graph -- a three tier graph, each tier it "should transform arbitrarily deep graphs into isomorphic trees" do config = Puppet::Resource::Catalog.new("mynode") @scope = mkscope @scope.stubs(:tags).returns([]) @source = mock 'source' # Create our scopes. top = mkresource "class", :main topbucket = [] topbucket.expects(:classes=).with([]) top.expects(:to_trans).returns(topbucket) topres = mkresource "file", "/top" topres.expects(:to_trans).returns(:topres) config.add_edge top, topres middle = mkresource "class", "middle" middle.expects(:to_trans).returns([]) config.add_edge top, middle midres = mkresource "file", "/mid" midres.expects(:to_trans).returns(:midres) config.add_edge middle, midres bottom = mkresource "class", "bottom" bottom.expects(:to_trans).returns([]) config.add_edge middle, bottom botres = mkresource "file", "/bot" botres.expects(:to_trans).returns(:botres) config.add_edge bottom, botres toparray = config.extract_to_transportable # This is annoying; it should look like: # [[[:botres], :midres], :topres] # but we can't guarantee sort order. toparray.include?(:topres).should be_true midarray = toparray.find { |t| t.is_a?(Array) } midarray.include?(:midres).should be_true botarray = midarray.find { |t| t.is_a?(Array) } botarray.include?(:botres).should be_true end end describe " when converting to a Puppet::Resource catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::TransObject.new 'top', "class" @topobject = Puppet::TransObject.new '/topobject', "file" @middle = Puppet::TransObject.new 'middle', "class" @middleobject = Puppet::TransObject.new '/middleobject', "file" @bottom = Puppet::TransObject.new 'bottom', "class" @bottomobject = Puppet::TransObject.new '/bottomobject', "file" @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_resource end it "should copy over the version" do @original.version = "foo" @original.to_resource.version.should == "foo" end it "should add all resources as Puppet::Resource instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Resource) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end end describe "when converting to a RAL catalog" do before do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @top = Puppet::Resource.new :class, 'top' @topobject = Puppet::Resource.new :file, '/topobject' @middle = Puppet::Resource.new :class, 'middle' @middleobject = Puppet::Resource.new :file, '/middleobject' @bottom = Puppet::Resource.new :class, 'bottom' @bottomobject = Puppet::Resource.new :file, '/bottomobject' @resources = [@top, @topobject, @middle, @middleobject, @bottom, @bottomobject] @original.add_resource(*@resources) @original.add_edge(@top, @topobject) @original.add_edge(@top, @middle) @original.add_edge(@middle, @middleobject) @original.add_edge(@middle, @bottom) @original.add_edge(@bottom, @bottomobject) @catalog = @original.to_ral end it "should add all resources as RAL instances" do @resources.each { |resource| @catalog.resource(resource.ref).should be_instance_of(Puppet::Type) } end it "should copy the tag list to the new catalog" do @catalog.tags.sort.should == @original.tags.sort end it "should copy the class list to the new catalog" do @catalog.classes.should == @original.classes end it "should duplicate the original edges" do @original.edges.each do |edge| @catalog.edge?(@catalog.resource(edge.source.ref), @catalog.resource(edge.target.ref)).should be_true end end it "should set itself as the catalog for each converted resource" do @catalog.vertices.each { |v| v.catalog.object_id.should equal(@catalog.object_id) } end # This tests #931. it "should not lose track of resources whose names vary" do changer = Puppet::TransObject.new 'changer', 'test' config = Puppet::Resource::Catalog.new('test') config.add_resource(changer) config.add_resource(@top) config.add_edge(@top, changer) resource = stub 'resource', :name => "changer2", :title => "changer2", :ref => "Test[changer2]", :catalog= => nil, :remove => nil #changer is going to get duplicated as part of a fix for aliases 1094 changer.expects(:dup).returns(changer) changer.expects(:to_ral).returns(resource) newconfig = nil proc { @catalog = config.to_ral }.should_not raise_error @catalog.resource("Test[changer2]").should equal(resource) end after do # Remove all resource instances. @catalog.clear(true) end end describe "when filtering" do before :each do @original = Puppet::Resource::Catalog.new("mynode") @original.tag(*%w{one two three}) @original.add_class *%w{four five six} @r1 = stub_everything 'r1', :ref => "File[/a]" @r1.stubs(:respond_to?).with(:ref).returns(true) @r1.stubs(:dup).returns(@r1) @r1.stubs(:is_a?).returns(Puppet::Resource).returns(true) @r2 = stub_everything 'r2', :ref => "File[/b]" @r2.stubs(:respond_to?).with(:ref).returns(true) @r2.stubs(:dup).returns(@r2) @r2.stubs(:is_a?).returns(Puppet::Resource).returns(true) @resources = [@r1,@r2] @original.add_resource(@r1,@r2) end it "should transform the catalog to a resource catalog" do @original.expects(:to_catalog).with { |h,b| h == :to_resource } @original.filter end it "should scan each catalog resource in turn and apply filtering block" do @resources.each { |r| r.expects(:test?) } @original.filter do |r| r.test? end end it "should filter out resources which produce true when the filter block is evaluated" do @original.filter do |r| r == @r1 end.resource("File[/a]").should be_nil end it "should not consider edges against resources that were filtered out" do @original.add_edge(@r1,@r2) @original.filter do |r| r == @r1 end.edge(@r1,@r2).should be_empty end end describe "when functioning as a resource container" do before do @catalog = Puppet::Resource::Catalog.new("host") @one = Puppet::Type.type(:notify).new :name => "one" @two = Puppet::Type.type(:notify).new :name => "two" @dupe = Puppet::Type.type(:notify).new :name => "one" end it "should provide a method to add one or more resources" do @catalog.add_resource @one, @two @catalog.resource(@one.ref).should equal(@one) @catalog.resource(@two.ref).should equal(@two) end it "should add resources to the relationship graph if it exists" do relgraph = @catalog.relationship_graph @catalog.add_resource @one relgraph.should be_vertex(@one) end it "should yield added resources if a block is provided" do yielded = [] @catalog.add_resource(@one, @two) { |r| yielded << r } yielded.length.should == 2 end it "should set itself as the resource's catalog if it is not a relationship graph" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should make all vertices available by resource reference" do @catalog.add_resource(@one) @catalog.resource(@one.ref).should equal(@one) @catalog.vertices.find { |r| r.ref == @one.ref }.should equal(@one) end it "should canonize how resources are referred to during retrieval when both type and title are provided" do @catalog.add_resource(@one) @catalog.resource("notify", "one").should equal(@one) end it "should canonize how resources are referred to during retrieval when just the title is provided" do @catalog.add_resource(@one) @catalog.resource("notify[one]", nil).should equal(@one) end it "should not allow two resources with the same resource reference" do @catalog.add_resource(@one) proc { @catalog.add_resource(@dupe) }.should raise_error(Puppet::Resource::Catalog::DuplicateResourceError) end it "should not store objects that do not respond to :ref" do proc { @catalog.add_resource("thing") }.should raise_error(ArgumentError) end it "should remove all resources when asked" do @catalog.add_resource @one @catalog.add_resource @two @one.expects :remove @two.expects :remove @catalog.clear(true) end it "should support a mechanism for finishing resources" do @one.expects :finish @two.expects :finish @catalog.add_resource @one @catalog.add_resource @two @catalog.finalize end it "should make default resources when finalizing" do @catalog.expects(:make_default_resources) @catalog.finalize end it "should add default resources to the catalog upon creation" do @catalog.make_default_resources @catalog.resource(:schedule, "daily").should_not be_nil end it "should optionally support an initialization block and should finalize after such blocks" do @one.expects :finish @two.expects :finish config = Puppet::Resource::Catalog.new("host") do |conf| conf.add_resource @one conf.add_resource @two end end it "should inform the resource that it is the resource's catalog" do @one.expects(:catalog=).with(@catalog) @catalog.add_resource @one end it "should be able to find resources by reference" do @catalog.add_resource @one @catalog.resource(@one.ref).should equal(@one) end it "should be able to find resources by reference or by type/title tuple" do @catalog.add_resource @one @catalog.resource("notify", "one").should equal(@one) end it "should have a mechanism for removing resources" do @catalog.add_resource @one @one.expects :remove @catalog.remove_resource(@one) @catalog.resource(@one.ref).should be_nil @catalog.vertex?(@one).should be_false end it "should have a method for creating aliases for resources" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should create aliases for resources isomorphic resources whose names do not match their titles" do resource = Puppet::Type::File.new(:title => "testing", :path => "/something") @catalog.add_resource(resource) @catalog.resource(:file, "/something").should equal(resource) end it "should not create aliases for resources non-isomorphic resources whose names do not match their titles" do resource = Puppet::Type.type(:exec).new(:title => "testing", :command => "echo", :path => %w{/bin /usr/bin /usr/local/bin}) @catalog.add_resource(resource) # Yay, I've already got a 'should' method @catalog.resource(:exec, "echo").object_id.should == nil.object_id end # This test is the same as the previous, but the behaviour should be explicit. it "should alias using the class name from the resource reference, not the resource class name" do @catalog.add_resource @one @catalog.alias(@one, "other") @catalog.resource("notify", "other").should equal(@one) end it "should ignore conflicting aliases that point to the aliased resource" do @catalog.alias(@one, "other") lambda { @catalog.alias(@one, "other") }.should_not raise_error end it "should fail to add an alias if the aliased name already exists" do @catalog.add_resource @one proc { @catalog.alias @two, "one" }.should raise_error(ArgumentError) end it "should not fail when a resource has duplicate aliases created" do @catalog.add_resource @one proc { @catalog.alias @one, "one" }.should_not raise_error end it "should not create aliases that point back to the resource" do @catalog.alias(@one, "one") @catalog.resource(:notify, "one").should be_nil end it "should be able to look resources up by their aliases" do @catalog.add_resource @one @catalog.alias @one, "two" @catalog.resource(:notify, "two").should equal(@one) end it "should remove resource aliases when the target resource is removed" do @catalog.add_resource @one @catalog.alias(@one, "other") @one.expects :remove @catalog.remove_resource(@one) @catalog.resource("notify", "other").should be_nil end it "should add an alias for the namevar when the title and name differ on isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => "/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(true) @catalog.add_resource(resource) @catalog.resource(:file, "other").should equal(resource) @catalog.resource(:file, "/something").ref.should == resource.ref end it "should not add an alias for the namevar when the title and name differ on non-isomorphic resource types" do resource = Puppet::Type.type(:file).new :path => "/something", :title => "other", :content => "blah" resource.expects(:isomorphic?).returns(false) @catalog.add_resource(resource) @catalog.resource(:file, resource.title).should equal(resource) # We can't use .should here, because the resources respond to that method. if @catalog.resource(:file, resource.name) raise "Aliased non-isomorphic resource" end end it "should provide a method to create additional resources that also registers the resource" do args = {:name => "/yay", :ensure => :file} resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" Puppet::Type.type(:file).expects(:new).with(args).returns(resource) @catalog.create_resource :file, args @catalog.resource("File[/yay]").should equal(resource) end end describe "when applying" do before :each do @catalog = Puppet::Resource::Catalog.new("host") @transaction = mock 'transaction' Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:cleanup) @transaction.stubs(:add_times) end it "should create and evaluate a transaction" do @transaction.expects(:evaluate) @catalog.apply end it "should provide the catalog retrieval time to the transaction" do @catalog.retrieval_duration = 5 @transaction.expects(:add_times).with(:config_retrieval => 5) @catalog.apply end it "should use a retrieval time of 0 if none is set in the catalog" do @catalog.retrieval_duration = nil @transaction.expects(:add_times).with(:config_retrieval => 0) @catalog.apply end it "should clean up the transaction" do @transaction.expects :cleanup @catalog.apply end it "should return the transaction" do @catalog.apply.should equal(@transaction) end it "should yield the transaction if a block is provided" do @catalog.apply do |trans| trans.should equal(@transaction) end end it "should default to not being a host catalog" do @catalog.host_config.should be_nil end it "should pass supplied tags on to the transaction" do @transaction.expects(:tags=).with(%w{one two}) @catalog.apply(:tags => %w{one two}) end it "should set ignoreschedules on the transaction if specified in apply()" do @transaction.expects(:ignoreschedules=).with(true) @catalog.apply(:ignoreschedules => true) end it "should remove resources created mid-transaction" do args = {:name => "/yay", :ensure => :file} resource = stub 'file', :ref => "File[/yay]", :catalog= => @catalog, :title => "/yay", :[] => "/yay" @transaction = mock 'transaction' Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:cleanup) @transaction.stubs(:add_times) Puppet::Type.type(:file).expects(:new).with(args).returns(resource) resource.expects :remove @catalog.apply do |trans| @catalog.create_resource :file, args @catalog.resource("File[/yay]").should equal(resource) end @catalog.resource("File[/yay]").should be_nil end it "should remove resources added mid-transaction" do @transaction = mock 'transaction' Puppet::Transaction.stubs(:new).returns(@transaction) @transaction.stubs(:evaluate) @transaction.stubs(:cleanup) @transaction.stubs(:add_times) file = Puppet::Type.type(:file).new(:name => "/yay", :ensure => :file) @catalog.apply do |trans| @catalog.add_resource file @catalog.resource("File[/yay]").should_not be_nil end @catalog.resource("File[/yay]").should be_nil end it "should expire cached data in the resources both before and after the transaction" do @catalog.expects(:expire).times(2) @catalog.apply end describe "host catalogs" do # super() doesn't work in the setup method for some reason before do @catalog.host_config = true Puppet::Util::Storage.stubs(:store) end it "should initialize the state database before applying a catalog" do Puppet::Util::Storage.expects(:load) # Short-circuit the apply, so we know we're loading before the transaction Puppet::Transaction.expects(:new).raises ArgumentError proc { @catalog.apply }.should raise_error(ArgumentError) end it "should sync the state database after applying" do Puppet::Util::Storage.expects(:store) @transaction.stubs :any_failed? => false @catalog.apply end after { Puppet.settings.clear } end describe "non-host catalogs" do before do @catalog.host_config = false end it "should never send reports" do Puppet[:report] = true Puppet[:summarize] = true @catalog.apply end it "should never modify the state database" do Puppet::Util::Storage.expects(:load).never Puppet::Util::Storage.expects(:store).never @catalog.apply end after { Puppet.settings.clear } end end describe "when creating a relationship graph" do before do Puppet::Type.type(:component) @catalog = Puppet::Resource::Catalog.new("host") @compone = Puppet::Type::Component.new :name => "one" @comptwo = Puppet::Type::Component.new :name => "two", :require => "Class[one]" @file = Puppet::Type.type(:file) @one = @file.new :path => "/one" @two = @file.new :path => "/two" @sub = @file.new :path => "/two/subdir" @catalog.add_edge @compone, @one @catalog.add_edge @comptwo, @two @three = @file.new :path => "/three" @four = @file.new :path => "/four", :require => "File[/three]" @five = @file.new :path => "/five" @catalog.add_resource @compone, @comptwo, @one, @two, @three, @four, @five, @sub @relationships = @catalog.relationship_graph end it "should be able to create a relationship graph" do @relationships.should be_instance_of(Puppet::SimpleGraph) end it "should not have any components" do @relationships.vertices.find { |r| r.instance_of?(Puppet::Type::Component) }.should be_nil end it "should have all non-component resources from the catalog" do # The failures print out too much info, so i just do a class comparison @relationships.vertex?(@five).should be_true end it "should have all resource relationships set as edges" do @relationships.edge?(@three, @four).should be_true end it "should copy component relationships to all contained resources" do @relationships.edge?(@one, @two).should be_true end it "should add automatic relationships to the relationship graph" do @relationships.edge?(@two, @sub).should be_true end it "should get removed when the catalog is cleaned up" do @relationships.expects(:clear) @catalog.clear @catalog.instance_variable_get("@relationship_graph").should be_nil end it "should write :relationships and :expanded_relationships graph files if the catalog is a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).with(:relationships) graph.expects(:write_graph).with(:expanded_relationships) @catalog.host_config = true @catalog.relationship_graph end it "should not write graph files if the catalog is not a host catalog" do @catalog.clear graph = Puppet::SimpleGraph.new Puppet::SimpleGraph.expects(:new).returns graph graph.expects(:write_graph).never @catalog.host_config = false @catalog.relationship_graph end it "should create a new relationship graph after clearing the old one" do @relationships.expects(:clear) @catalog.clear @catalog.relationship_graph.should be_instance_of(Puppet::SimpleGraph) end it "should remove removed resources from the relationship graph if it exists" do @catalog.remove_resource(@one) @catalog.relationship_graph.vertex?(@one).should be_false end end describe "when writing dot files" do before do @catalog = Puppet::Resource::Catalog.new("host") @name = :test @file = File.join(Puppet[:graphdir], @name.to_s + ".dot") end it "should only write when it is a host catalog" do File.expects(:open).with(@file).never @catalog.host_config = false Puppet[:graph] = true @catalog.write_graph(@name) end after do Puppet.settings.clear end end describe "when indirecting" do before do @indirection = stub 'indirection', :name => :catalog Puppet::Util::Cacher.expire end it "should redirect to the indirection for retrieval" do Puppet::Resource::Catalog.stubs(:indirection).returns(@indirection) @indirection.expects(:find) Puppet::Resource::Catalog.find(:myconfig) end it "should default to the 'compiler' terminus" do Puppet::Resource::Catalog.indirection.terminus_class.should == :compiler end after do Puppet::Util::Cacher.expire end end describe "when converting to yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") end it "should be able to be dumped to yaml" do YAML.dump(@catalog).should be_instance_of(String) end end describe "when converting from yaml" do before do @catalog = Puppet::Resource::Catalog.new("me") @catalog.add_edge("one", "two") text = YAML.dump(@catalog) @newcatalog = YAML.load(text) end it "should get converted back to a catalog" do @newcatalog.should be_instance_of(Puppet::Resource::Catalog) end it "should have all vertices" do @newcatalog.vertex?("one").should be_true @newcatalog.vertex?("two").should be_true end it "should have all edges" do @newcatalog.edge?("one", "two").should be_true end end end describe Puppet::Resource::Catalog, "when converting to pson" do confine "Missing 'pson' library" => Puppet.features.pson? before do @catalog = Puppet::Resource::Catalog.new("myhost") end def pson_output_should @catalog.class.expects(:pson_create).with { |hash| yield hash }.returns(:something) end # LAK:NOTE For all of these tests, we convert back to the resource so we can # trap the actual data structure then. it "should set its document_type to 'Catalog'" do pson_output_should { |hash| hash['document_type'] == "Catalog" } PSON.parse @catalog.to_pson end it "should set its data as a hash" do pson_output_should { |hash| hash['data'].is_a?(Hash) } PSON.parse @catalog.to_pson end [:name, :version, :tags, :classes].each do |param| it "should set its #{param} to the #{param} of the resource" do @catalog.send(param.to_s + "=", "testing") unless @catalog.send(param) pson_output_should { |hash| hash['data'][param.to_s] == @catalog.send(param) } PSON.parse @catalog.to_pson end end it "should convert its resources to a PSON-encoded array and store it as the 'resources' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => "Foo[one]" two = stub 'two', :to_pson_data_hash => "two_resource", :ref => "Foo[two]" @catalog.add_resource(one) @catalog.add_resource(two) # TODO this should really guarantee sort order PSON.parse(@catalog.to_pson,:create_additions => false)['data']['resources'].sort.should == ["one_resource", "two_resource"].sort end it "should convert its edges to a PSON-encoded array and store it as the 'edges' data" do one = stub 'one', :to_pson_data_hash => "one_resource", :ref => 'Foo[one]' two = stub 'two', :to_pson_data_hash => "two_resource", :ref => 'Foo[two]' three = stub 'three', :to_pson_data_hash => "three_resource", :ref => 'Foo[three]' @catalog.add_edge(one, two) @catalog.add_edge(two, three) @catalog.edge(one, two ).expects(:to_pson_data_hash).returns "one_two_pson" @catalog.edge(two, three).expects(:to_pson_data_hash).returns "two_three_pson" PSON.parse(@catalog.to_pson,:create_additions => false)['data']['edges'].sort.should == %w{one_two_pson two_three_pson}.sort end end describe Puppet::Resource::Catalog, "when converting from pson" do confine "Missing 'pson' library" => Puppet.features.pson? def pson_result_should Puppet::Resource::Catalog.expects(:new).with { |hash| yield hash } end before do @data = { 'name' => "myhost" } @pson = { 'document_type' => 'Puppet::Resource::Catalog', 'data' => @data, 'metadata' => {} } @catalog = Puppet::Resource::Catalog.new("myhost") Puppet::Resource::Catalog.stubs(:new).returns @catalog end it "should be extended with the PSON utility module" do Puppet::Resource::Catalog.metaclass.ancestors.should be_include(Puppet::Util::Pson) end it "should create it with the provided name" do Puppet::Resource::Catalog.expects(:new).with('myhost').returns @catalog PSON.parse @pson.to_pson end it "should set the provided version on the catalog if one is set" do @data['version'] = 50 PSON.parse @pson.to_pson @catalog.version.should == @data['version'] end it "should set any provided tags on the catalog" do @data['tags'] = %w{one two} PSON.parse @pson.to_pson @catalog.tags.should == @data['tags'] end it "should set any provided classes on the catalog" do @data['classes'] = %w{one two} PSON.parse @pson.to_pson @catalog.classes.should == @data['classes'] end it 'should convert the resources list into resources and add each of them' do @data['resources'] = [Puppet::Resource.new(:file, "/foo"), Puppet::Resource.new(:file, "/bar")] @catalog.expects(:add_resource).times(2).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert resources even if they do not include "type" information' do @data['resources'] = [Puppet::Resource.new(:file, "/foo")] @data['resources'][0].expects(:to_pson).returns '{"title":"/foo","tags":["file"],"type":"File"}' @catalog.expects(:add_resource).with { |res| res.type == "File" } PSON.parse @pson.to_pson end it 'should convert the edges list into edges and add each of them' do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") two = Puppet::Relationship.new("tsource", "ttarget", :event => "two", :callback => "refresh") @data['edges'] = [one, two] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } @catalog.expects(:add_edge).with { |edge| edge.event == "two" } PSON.parse @pson.to_pson end it "should be able to convert relationships that do not include 'type' information" do one = Puppet::Relationship.new("osource", "otarget", :event => "one", :callback => "refresh") one.expects(:to_pson).returns "{\"event\":\"one\",\"callback\":\"refresh\",\"source\":\"osource\",\"target\":\"otarget\"}" @data['edges'] = [one] @catalog.stubs(:resource).returns("eh") @catalog.expects(:add_edge).with { |edge| edge.event == "one" } PSON.parse @pson.to_pson end it "should set the source and target for each edge to the actual resource" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns("target_resource") @catalog.expects(:add_edge).with { |edge| edge.source == "source_resource" and edge.target == "target_resource" } PSON.parse @pson.to_pson end it "should fail if the source resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.expects(:resource).with("source").returns(nil) @catalog.stubs(:resource).with("target").returns("target_resource") lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end it "should fail if the target resource cannot be found" do edge = Puppet::Relationship.new("source", "target") @data['edges'] = [edge] @catalog.stubs(:resource).with("source").returns("source_resource") @catalog.expects(:resource).with("target").returns(nil) lambda { PSON.parse @pson.to_pson }.should raise_error(ArgumentError) end end