From fd0c5cbddec8dc53196a3b84e33e1000c3c0720f Mon Sep 17 00:00:00 2001 From: Luke Kanies Date: Mon, 11 Feb 2008 17:59:34 -0600 Subject: Changing the name of the Compile class to Compiler, since it's stupid to have a class named after a verb. --- lib/puppet/parser/ast/collection.rb | 2 +- lib/puppet/parser/ast/definition.rb | 2 +- lib/puppet/parser/ast/hostclass.rb | 6 +- lib/puppet/parser/ast/node.rb | 2 +- lib/puppet/parser/ast/resource.rb | 4 +- lib/puppet/parser/ast/resource_override.rb | 2 +- lib/puppet/parser/collector.rb | 8 +- lib/puppet/parser/compile.rb | 467 ----------------------------- lib/puppet/parser/compiler.rb | 467 +++++++++++++++++++++++++++++ lib/puppet/parser/functions.rb | 6 +- lib/puppet/parser/interpreter.rb | 4 +- lib/puppet/parser/scope.rb | 16 +- lib/puppet/parser/templatewrapper.rb | 2 +- 13 files changed, 494 insertions(+), 494 deletions(-) delete mode 100644 lib/puppet/parser/compile.rb create mode 100644 lib/puppet/parser/compiler.rb (limited to 'lib/puppet/parser') diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb index 31f508929..9e795a33c 100644 --- a/lib/puppet/parser/ast/collection.rb +++ b/lib/puppet/parser/ast/collection.rb @@ -18,7 +18,7 @@ class Collection < AST::Branch newcoll = Puppet::Parser::Collector.new(scope, @type, str, code, self.form) - scope.compile.add_collection(newcoll) + scope.compiler.add_collection(newcoll) newcoll end diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb index 992bb1f5e..b4a90016a 100644 --- a/lib/puppet/parser/ast/definition.rb +++ b/lib/puppet/parser/ast/definition.rb @@ -31,7 +31,7 @@ class Puppet::Parser::AST::Definition < Puppet::Parser::AST::Branch scope.catalog.tag(*resource.tags) - scope.compile.add_resource(scope, resource) + scope.compiler.add_resource(scope, resource) return resource end diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb index 4f2d00f0c..f49016526 100644 --- a/lib/puppet/parser/ast/hostclass.rb +++ b/lib/puppet/parser/ast/hostclass.rb @@ -32,7 +32,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition scope = resource.scope # Verify that we haven't already been evaluated. This is # what provides the singleton aspect. - if existing_scope = scope.compile.class_scope(self) + if existing_scope = scope.compiler.class_scope(self) Puppet.debug "Class '%s' already evaluated; not evaluating again" % (classname == "" ? "main" : classname) return nil end @@ -57,7 +57,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition # Set the class before we do anything else, so that it's set # during the evaluation and can be inspected. - scope.compile.class_set(self.classname, scope) + scope.compiler.class_set(self.classname, scope) # Now evaluate our code, yo. if self.code @@ -68,7 +68,7 @@ class Puppet::Parser::AST::HostClass < Puppet::Parser::AST::Definition end def parent_scope(scope, klass) - if s = scope.compile.class_scope(klass) + if s = scope.compiler.class_scope(klass) return s else raise Puppet::DevError, "Could not find scope for %s" % klass.classname diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb index 7ff7a18e1..8cebac8a8 100644 --- a/lib/puppet/parser/ast/node.rb +++ b/lib/puppet/parser/ast/node.rb @@ -25,7 +25,7 @@ class Puppet::Parser::AST::Node < Puppet::Parser::AST::HostClass # Mark our node name as a class, too, but strip it of the domain # name. Make the mark before we evaluate the code, so that it is # marked within the code itself. - scope.compile.class_set(self.classname, scope) + scope.compiler.class_set(self.classname, scope) # And then evaluate our code if we have any @code.safeevaluate(scope) if self.code diff --git a/lib/puppet/parser/ast/resource.rb b/lib/puppet/parser/ast/resource.rb index 2dadf9ed6..8a60522a3 100644 --- a/lib/puppet/parser/ast/resource.rb +++ b/lib/puppet/parser/ast/resource.rb @@ -48,10 +48,10 @@ class Resource < AST::ResourceReference :scope => scope ) - # And then store the resource in the compile. + # And then store the resource in the compiler. # At some point, we need to switch all of this to return # objects instead of storing them like this. - scope.compile.add_resource(scope, obj) + scope.compiler.add_resource(scope, obj) obj end }.reject { |obj| obj.nil? } diff --git a/lib/puppet/parser/ast/resource_override.rb b/lib/puppet/parser/ast/resource_override.rb index db0986a8e..f9464acda 100644 --- a/lib/puppet/parser/ast/resource_override.rb +++ b/lib/puppet/parser/ast/resource_override.rb @@ -42,7 +42,7 @@ class Puppet::Parser::AST # Now we tell the scope that it's an override, and it behaves as # necessary. - scope.compile.add_override(obj) + scope.compiler.add_override(obj) obj end diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb index efd64a320..e0c37cd35 100644 --- a/lib/puppet/parser/collector.rb +++ b/lib/puppet/parser/collector.rb @@ -118,20 +118,20 @@ class Puppet::Parser::Collector # If there are no more resources to find, delete this from the list # of collections. if @resources.empty? - @scope.compile.delete_collection(self) + @scope.compiler.delete_collection(self) end return result end - # Collect just virtual objects, from our local compile. + # Collect just virtual objects, from our local compiler. def collect_virtual(exported = false) if exported method = :exported? else method = :virtual? end - scope.compile.resources.find_all do |resource| + scope.compiler.resources.find_all do |resource| resource.type == @type and resource.send(method) and match?(resource) end end @@ -150,7 +150,7 @@ class Puppet::Parser::Collector resource.exported = false - scope.compile.add_resource(scope, resource) + scope.compiler.add_resource(scope, resource) return resource end diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb deleted file mode 100644 index 2415fd5e8..000000000 --- a/lib/puppet/parser/compile.rb +++ /dev/null @@ -1,467 +0,0 @@ -# Created by Luke A. Kanies on 2007-08-13. -# Copyright (c) 2007. All rights reserved. - -require 'puppet/node' -require 'puppet/node/catalog' -require 'puppet/util/errors' - -# Maintain a graph of scopes, along with a bunch of data -# about the individual catalog we're compiling. -class Puppet::Parser::Compile - include Puppet::Util - include Puppet::Util::Errors - attr_reader :parser, :node, :facts, :collections, :catalog, :node_scope - - # 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) - @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. - @catalog.add_edge!(scope.resource, resource) - end - - # Do we use nodes found in the code, vs. the external node sources? - def ast_nodes? - parser.nodes.length > 0 - end - - # Store the fact that we've evaluated a class, and store a reference to - # the scope in which it was evaluated, so that we can look it up later. - def class_set(name, scope) - if existing = @class_scopes[name] - if existing.nodescope? or scope.nodescope? - raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" - else - raise Puppet::DevError, "Somehow evaluated the same class twice" - end - end - @class_scopes[name] = scope - @catalog.add_class(name) unless name == "" - end - - # Return the scope associated with a class. This is just here so - # that subclasses can set their parent scopes to be the scope of - # their parent class, and it's also used when looking up qualified - # variables. - def class_scope(klass) - # They might pass in either the class or class name - if klass.respond_to?(:classname) - @class_scopes[klass.classname] - else - @class_scopes[klass] - end - end - - # Return a list of all of the defined classes. - def classlist - return @catalog.classes - end - - # Compile 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() - - if Puppet[:storeconfigs] - store() - end - - 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.findclass(name) - found << name and next if class_scope(klass) - - resource = klass.evaluate(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 - - # Set up our compile. We require a parser - # and a node object; the parser is so we can look up classes - # and AST nodes, and the node has all of the client's info, - # like facts and environment. - def initialize(node, parser, options = {}) - @node = node - @parser = parser - - options.each do |param, value| - begin - send(param.to_s + "=", value) - rescue NoMethodError - raise ArgumentError, "Compile 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. Adds an edge between the scope and - # its parent to the graph. - def newscope(parent, options = {}) - parent ||= topscope - options[:compile] = self - options[:parser] ||= self.parser - scope = Puppet::Parser::Scope.new(options) - @scope_graph.add_edge!(parent, scope) - scope - end - - # Find the parent of a given scope. Assumes scopes only ever have - # one in edge, which will always be true. - def parent(scope) - if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0 - ary[0] - else - nil - end - end - - # Return any overrides for the given resource. - def resource_overrides(resource) - @resource_overrides[resource.ref] - end - - # Return a list of all resources. - def resources - @catalog.vertices - 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 = @parser.nodes[name.to_s.downcase] - end - - unless (astnode ||= @parser.nodes["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.evaluate(topscope) - - resource.evaluate - - # Now set the node scope appropriately, so that :topscope can - # behave differently. - @node_scope = 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 - ary.each do |resource| - resource.evaluate - end - # If we evaluated, let the loop know. - return true - 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 = @parser.findclass("", "") || @parser.newclass("") - @topscope.source = @main - @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) - @topscope.resource = @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 object(s) %s" % 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 - @catalog.resources.each do |name| - resource = @catalog.resource(name) - - # 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(:compile => self, :parser => self.parser) - @scope_graph.add_vertex!(@topscope) - end - - # Set up all of our internal variables. - def initvars - # The table for storing class singletons. This will only actually - # be used by top scopes and node scopes. - @class_scopes = {} - - # 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 = [] - - # A graph for maintaining scope relationships. - @scope_graph = Puppet::SimpleGraph.new - - # For maintaining the relationship between scopes and their resources. - @catalog = Puppet::Node::Catalog.new(@node.name) - @catalog.version = @parser.version - 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 - end - - # Store the catalog into the database. - def store - unless Puppet.features.rails? - raise Puppet::Error, - "storeconfigs is enabled but rails is unavailable" - end - - unless ActiveRecord::Base.connected? - Puppet::Rails.connect - end - - # We used to have hooks here for forking and saving, but I don't - # think it's worth retaining at this point. - store_to_active_record(@node, @catalog.vertices) - end - - # Do the actual storage. - def store_to_active_record(node, resources) - begin - # We store all of the objects, even the collectable ones - benchmark(:info, "Stored catalog for #{node.name}") do - Puppet::Rails::Host.transaction do - Puppet::Rails::Host.store(node, resources) - end - end - rescue => detail - if Puppet[:trace] - puts detail.backtrace - end - Puppet.err "Could not store configs: %s" % detail.to_s - end - 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 = @catalog.vertices.reject { |resource| resource.builtin? or resource.evaluated? } - - if ary.empty? - return nil - else - return ary - end - end -end diff --git a/lib/puppet/parser/compiler.rb b/lib/puppet/parser/compiler.rb new file mode 100644 index 000000000..27860487a --- /dev/null +++ b/lib/puppet/parser/compiler.rb @@ -0,0 +1,467 @@ +# Created by Luke A. Kanies on 2007-08-13. +# Copyright (c) 2007. All rights reserved. + +require 'puppet/node' +require 'puppet/node/catalog' +require 'puppet/util/errors' + +# 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 + attr_reader :parser, :node, :facts, :collections, :catalog, :node_scope + + # 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) + @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. + @catalog.add_edge!(scope.resource, resource) + end + + # Do we use nodes found in the code, vs. the external node sources? + def ast_nodes? + parser.nodes.length > 0 + end + + # Store the fact that we've evaluated a class, and store a reference to + # the scope in which it was evaluated, so that we can look it up later. + def class_set(name, scope) + if existing = @class_scopes[name] + if existing.nodescope? or scope.nodescope? + raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name" + else + raise Puppet::DevError, "Somehow evaluated the same class twice" + end + end + @class_scopes[name] = scope + @catalog.add_class(name) unless name == "" + end + + # Return the scope associated with a class. This is just here so + # that subclasses can set their parent scopes to be the scope of + # their parent class, and it's also used when looking up qualified + # variables. + def class_scope(klass) + # They might pass in either the class or class name + if klass.respond_to?(:classname) + @class_scopes[klass.classname] + else + @class_scopes[klass] + end + 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() + + if Puppet[:storeconfigs] + store() + end + + 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.findclass(name) + found << name and next if class_scope(klass) + + resource = klass.evaluate(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 + + # Set up our compile. We require a parser + # and a node object; the parser is so we can look up classes + # and AST nodes, and the node has all of the client's info, + # like facts and environment. + def initialize(node, parser, options = {}) + @node = node + @parser = parser + + 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. Adds an edge between the scope and + # its parent to the graph. + def newscope(parent, options = {}) + parent ||= topscope + options[:compiler] = self + options[:parser] ||= self.parser + scope = Puppet::Parser::Scope.new(options) + @scope_graph.add_edge!(parent, scope) + scope + end + + # Find the parent of a given scope. Assumes scopes only ever have + # one in edge, which will always be true. + def parent(scope) + if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0 + ary[0] + else + nil + end + end + + # Return any overrides for the given resource. + def resource_overrides(resource) + @resource_overrides[resource.ref] + end + + # Return a list of all resources. + def resources + @catalog.vertices + 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 = @parser.nodes[name.to_s.downcase] + end + + unless (astnode ||= @parser.nodes["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.evaluate(topscope) + + resource.evaluate + + # Now set the node scope appropriately, so that :topscope can + # behave differently. + @node_scope = 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 + ary.each do |resource| + resource.evaluate + end + # If we evaluated, let the loop know. + return true + 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 = @parser.findclass("", "") || @parser.newclass("") + @topscope.source = @main + @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main) + @topscope.resource = @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 object(s) %s" % 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 + @catalog.resources.each do |name| + resource = @catalog.resource(name) + + # 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, :parser => self.parser) + @scope_graph.add_vertex!(@topscope) + end + + # Set up all of our internal variables. + def initvars + # The table for storing class singletons. This will only actually + # be used by top scopes and node scopes. + @class_scopes = {} + + # 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 = [] + + # A graph for maintaining scope relationships. + @scope_graph = Puppet::SimpleGraph.new + + # For maintaining the relationship between scopes and their resources. + @catalog = Puppet::Node::Catalog.new(@node.name) + @catalog.version = @parser.version + 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 + end + + # Store the catalog into the database. + def store + unless Puppet.features.rails? + raise Puppet::Error, + "storeconfigs is enabled but rails is unavailable" + end + + unless ActiveRecord::Base.connected? + Puppet::Rails.connect + end + + # We used to have hooks here for forking and saving, but I don't + # think it's worth retaining at this point. + store_to_active_record(@node, @catalog.vertices) + end + + # Do the actual storage. + def store_to_active_record(node, resources) + begin + # We store all of the objects, even the collectable ones + benchmark(:info, "Stored catalog for #{node.name}") do + Puppet::Rails::Host.transaction do + Puppet::Rails::Host.store(node, resources) + end + end + rescue => detail + if Puppet[:trace] + puts detail.backtrace + end + Puppet.err "Could not store configs: %s" % detail.to_s + end + 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 = @catalog.vertices.reject { |resource| resource.builtin? or resource.evaluated? } + + if ary.empty? + return nil + else + return ary + end + end +end diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb index 34b38b809..e0b60e161 100644 --- a/lib/puppet/parser/functions.rb +++ b/lib/puppet/parser/functions.rb @@ -111,7 +111,7 @@ module Functions vals = [vals] unless vals.is_a?(Array) # The 'false' disables lazy evaluation. - klasses = compile.evaluate_classes(vals, self, false) + klasses = compiler.evaluate_classes(vals, self, false) missing = vals.find_all do |klass| ! klasses.include?(klass) @@ -146,7 +146,7 @@ module Functions tells you whether the current container is tagged with the specified tags. The tags are ANDed, so that all of the specified tags must be included for the function to return true.") do |vals| - configtags = compile.catalog.tags + configtags = compiler.catalog.tags resourcetags = resource.tags retval = true @@ -235,7 +235,7 @@ module Functions vals = [vals] unless vals.is_a?(Array) coll.resources = vals - compile.add_collection(coll) + compiler.add_collection(coll) end newfunction(:search, :doc => "Add another namespace for this class to search. diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb index e29e19944..1d93193dd 100644 --- a/lib/puppet/parser/interpreter.rb +++ b/lib/puppet/parser/interpreter.rb @@ -3,7 +3,7 @@ require 'timeout' require 'puppet/rails' require 'puppet/util/methodhelper' require 'puppet/parser/parser' -require 'puppet/parser/compile' +require 'puppet/parser/compiler' require 'puppet/parser/scope' # The interpreter is a very simple entry-point class that @@ -25,7 +25,7 @@ class Puppet::Parser::Interpreter # evaluate our whole tree def compile(node) raise Puppet::ParseError, "Could not parse configuration; cannot compile" unless env_parser = parser(node.environment) - return Puppet::Parser::Compile.new(node, env_parser).compile + return Puppet::Parser::Compiler.new(node, env_parser).compile end # create our interpreter diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb index 81d4ac71a..a6e43e7b3 100644 --- a/lib/puppet/parser/scope.rb +++ b/lib/puppet/parser/scope.rb @@ -17,20 +17,20 @@ class Puppet::Parser::Scope include Puppet::Util::Errors attr_accessor :parent, :level, :parser, :source, :resource attr_accessor :base, :keyword, :nodescope - attr_accessor :top, :translated, :compile + attr_accessor :top, :translated, :compiler # A demeterific shortcut to the catalog. def catalog - compile.catalog + compiler.catalog end # Proxy accessors def host - @compile.node.name + @compiler.node.name end def interpreter - @compile.interpreter + @compiler.interpreter end # Is the value true? This allows us to control the definition of truth @@ -77,7 +77,7 @@ class Puppet::Parser::Scope end def findresource(string, name = nil) - compile.findresource(string, name) + compiler.findresource(string, name) end # Initialize our new scope. Defaults to having no parent. @@ -152,7 +152,7 @@ class Puppet::Parser::Scope unless klass raise Puppet::ParseError, "Could not find class %s" % klassname end - unless kscope = compile.class_scope(klass) + unless kscope = compiler.class_scope(klass) raise Puppet::ParseError, "Class %s has not been evaluated so its variables cannot be referenced" % klass.classname end return kscope.lookupvar(shortname, usestring) @@ -189,7 +189,7 @@ class Puppet::Parser::Scope # Create a new scope and set these options. def newscope(options = {}) - compile.newscope(self, options) + compiler.newscope(self, options) end # Is this class for a node? This is used to make sure that @@ -204,7 +204,7 @@ class Puppet::Parser::Scope # than doing lots of queries. def parent unless defined?(@parent) - @parent = compile.parent(self) + @parent = compiler.parent(self) end @parent end diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb index 13823d483..7a8f74156 100644 --- a/lib/puppet/parser/templatewrapper.rb +++ b/lib/puppet/parser/templatewrapper.rb @@ -7,7 +7,7 @@ class Puppet::Parser::TemplateWrapper def initialize(scope, file) @scope = scope - @file = Puppet::Module::find_template(file, @scope.compile.environment) + @file = Puppet::Module::find_template(file, @scope.compiler.environment) unless FileTest.exists?(@file) raise Puppet::ParseError, -- cgit